Shell Chain Post-Quantum Cryptography Guide
Shell Chain is built from the ground up with post-quantum cryptographic primitives, making it resistant to attacks from both classical and quantum computers.
See also: Quickstart Guide · Testnet Operator Guide · JSON-RPC API Reference · Native Account Abstraction Guide
Table of Contents
- Why Post-Quantum Cryptography Matters
- Algorithms Used
- Key Generation
- Keystore Format
- Address Derivation
- PQ-HD Wallets
- Signature Sizes and Performance
- Incompatibility with ECDSA and MetaMask
- Algorithm Agility and Legacy Compatibility
Why Post-Quantum Cryptography Matters
Traditional blockchains (Bitcoin, Ethereum) rely on ECDSA (Elliptic Curve Digital Signature Algorithm) for transaction signatures. ECDSA's security depends on the hardness of the elliptic curve discrete logarithm problem — a problem that quantum computers can solve efficiently using Shor's algorithm.
A sufficiently powerful quantum computer could:
- Forge signatures on any transaction by recovering private keys from public keys.
- Steal funds from any account whose public key has been revealed (i.e., any account that has ever sent a transaction).
- Rewrite history by forging block proposer signatures.
While large-scale quantum computers don't exist yet, the threat is real:
- NIST finalized the first post-quantum cryptography standards in 2024.
- "Harvest now, decrypt later" attacks mean adversaries can record blockchain traffic today and break it once quantum computers arrive.
- Key transitions take years — blockchains must migrate before quantum computers become practical.
Shell Chain eliminates this risk by using NIST-standardized lattice-based and hash-based signature schemes before Q-Day — no migration, no emergency hard fork needed.
Algorithms Used
ML-DSA-65 (Primary Signature Algorithm)
Shell Chain's current default signature algorithm is ML-DSA-65 (NIST FIPS 204). It preserves the established Dilithium3 wire format for backward compatibility, so the public-key and signature byte sizes remain unchanged while the chain standardizes on the finalized NIST name.
| Property | Value |
|---|---|
| Standard | NIST FIPS 204 (ML-DSA-65) |
| Security Level | NIST Level 3 (128-bit PQ) |
| Public Key Size | 1,952 bytes |
| Secret Key Size | 4,032 bytes |
| Signature Size | 3,309 bytes |
| Implementation note | Dilithium3 wire format retained for legacy compatibility |
Keccak-256 (Hashing)
Used for Ethereum-compatible hashing surfaces such as web3_sha3 and other
EVM-facing data structures. It is no longer used for Shell account address
derivation.
BLAKE3 (Internal Hashing)
Used for Shell account address derivation and other high-performance internal operations where Ethereum compatibility is not required.
address = blake3(algo_id || public_key) # full 32 bytes, no truncation
Argon2id (Key Derivation)
Used in the keystore for password-based key derivation:
| Parameter | Value |
|---|---|
| Memory | 64 MiB (65,536 KiB) |
| Iterations | 3 |
| Parallelism | 4 threads |
| Output | 32 bytes |
XChaCha20-Poly1305 (Keystore Encryption)
AEAD cipher used to encrypt private keys at rest. The 24-byte nonce is safe for random generation (no nonce reuse risk).
Key Generation
Command
shell-node key generate --output keystore.json
What happens internally
-
CSPRNG key generation — The
dilithium3::keypair()function generates a random keypair using the system's cryptographically secure random number generator. -
Address derivation — The 32-byte address is computed as:
address = blake3(algo_id || public_key) # full 32 bytes, no truncation display = "0x" + hex_lower(address) -
Password prompt — You enter an encryption password.
-
Key derivation — Argon2id derives a 32-byte encryption key from your password and a random 32-byte salt.
-
Encryption — The secret key is encrypted with XChaCha20-Poly1305 using the derived key and a random 24-byte nonce.
-
Keystore file — The encrypted key, public key, address, and all parameters are written to a JSON file.
Security properties
- Secret keys are zeroized in memory after use via the
zeroizecrate. When aDilithiumSigneris dropped, its secret key bytes are overwritten with zeros. - The derived encryption key is zeroized immediately after encrypting/decrypting.
- Each encryption uses a unique salt and nonce, so encrypting the same key with the same password produces different ciphertext.
Keystore Format
The keystore file is a JSON document inspired by the Ethereum Web3 Secret Storage format, adapted for post-quantum keys.
Structure
{
"version": 1,
"address": "0x0000000000000000000000000000000000000000000000000000000000000000", // 0x + 64 lowercase hex (32-byte BLAKE3)
"key_type": "mldsa65",
"kdf": "argon2id",
"kdf_params": {
"m_cost": 65536,
"t_cost": 3,
"p_cost": 4,
"salt": "0a1b2c3d...64_hex_chars"
},
"cipher": "xchacha20-poly1305",
"cipher_params": {
"nonce": "0a1b2c3d...48_hex_chars"
},
"ciphertext": "encrypted_secret_key_hex...",
"public_key": "mldsa65_public_key_hex..."
}
Field reference
| Field | Type | Description |
|---|---|---|
version |
u32 |
Format version (always 1) |
address |
String |
0x + 64 lowercase hex (32-byte BLAKE3) — canonical Shell Chain address format from v0.23.0 onward. |
key_type |
String |
"mldsa65" (primary), "dilithium3" (legacy compat), or "slh-dsa-sha2-256f" |
kdf |
String |
Key derivation function (always "argon2id") |
kdf_params.m_cost |
u32 |
Memory cost in KiB (65,536 = 64 MiB) |
kdf_params.t_cost |
u32 |
Time cost / iterations (3) |
kdf_params.p_cost |
u32 |
Parallelism degree (4) |
kdf_params.salt |
String |
32-byte random salt (hex) |
cipher |
String |
AEAD cipher (always "xchacha20-poly1305") |
cipher_params.nonce |
String |
24-byte random nonce (hex) |
ciphertext |
String |
Encrypted secret key (hex) |
public_key |
String |
Full public key (hex), used for verification |
Inspecting a keystore
shell-node key inspect keystore.json
# Output: Address: 0x9a3f...e2c1
This does not require the password. The keystore stores the address in
plaintext for compatibility, and CLI output uses the canonical 0x + 64-hex form.
Address Derivation
Shell Chain addresses are 32-byte BLAKE3 hashes of version ‖ algo_id ‖ public_key, rendered only as 0x + 64 lowercase hex characters. There is no truncation.
algo_id || public_key ──→ blake3() ──→ 32-byte hash ──→ hex-lowercase ──→ "0x" + 64 chars
Step by step
- Start with the signature algorithm ID and the raw PQ public key.
- Compute
blake3(algo_id || public_key)→ 32-byte hash. - Render the 32-byte address as lowercase hex with a
0xprefix for RPC, SDK APIs, and human-facing displays.
Algorithm IDs are implementation-defined; Shell Chain standardizes on ML-DSA-65 as primary, keeps Dilithium3 for legacy backward compatibility, and supports SLH-DSA-SHA2-256f as the fallback scheme.
Important notes
- The same public key always produces the same address (deterministic).
- The same public key under different supported algorithms produces different addresses because
algo_idis part of the preimage. - Different public keys produce different addresses (collision-resistant; the full 256-bit BLAKE3 output provides ~128-bit collision security and ~256-bit preimage security).
- Unlike Ethereum, the public key is an ML-DSA-65 key (1,952 bytes), not an ECDSA key (64 bytes). This means you cannot derive the public key from a signature as you can with ECDSA's
ecrecover. - The public key must be registered on-chain with the first transaction. Query it via
shell_getPqPubkey.
PQ-HD Wallets
Shell PQ-HD v1 gives wallets deterministic account and session-key derivation from one BIP-39 recovery phrase.
Recovery phrase and backup
- Default wallet generation uses a 24-word BIP-39 phrase.
- Back up the phrase offline before funding an account.
- The phrase and derived seed are encrypted locally by wallet software; they must never be sent to a dApp, RPC node, analytics endpoint, or support form.
- Restoring the same phrase derives the same account addresses and session-key addresses.
Account path
Primary ML-DSA-65 accounts use this hardened path shape:
m/9000'/8888'/1'/account'/change'/address'
9000'is the Shell PQ-HD purpose.8888'is the temporary Shell coin type pending SLIP-0044 registration.1'selects ML-DSA-65.- Wallet account 0 is
m/9000'/8888'/1'/0'/0'/0'.
Session-key path
AA session keys are deliberately isolated from normal account paths:
m/1'/1'/session_index'
Session keys are delegated keys for account-abstraction flows. A root account
signs a SessionAuth object that binds the session key to:
- chain ID
- expiry block
- value cap
- optional target address
If a wallet signs a specific AA transaction with the session key, the
session_signature field is filled. Otherwise the authorization can be kept as
a root-signed draft until the AA bundle signing hash is known.
Wallet safety rules
- Session keys do not replace the recovery phrase. Losing the phrase still loses the account.
- Use short expiry windows and conservative value caps.
- Restrict
targetwhen authorizing a dApp-specific session. - Rotate or clear account validation settings if a delegated session flow is suspected to be compromised.
Signature Sizes and Performance
Size comparison
| Algorithm | Public Key | Secret Key | Signature | PQ Security |
|---|---|---|---|---|
| ML-DSA-65 (Shell Chain primary) | 1,952 B | 4,032 B | 3,309 B | NIST Level 3 (128-bit) |
| SLH-DSA-SHA2-256f (Shell Chain fallback) | 32 B | 64 B | ~49,856 B | NIST Level 5 (256-bit) |
| ECDSA secp256k1 (Ethereum) | 64 B | 32 B | 64 B | 0-bit PQ (broken) |
| Ed25519 (Solana) | 32 B | 64 B | 64 B | 0-bit PQ (broken) |
ML-DSA-65 signatures are ~52× larger than ECDSA, but this is a necessary trade-off for quantum resistance.
Performance characteristics
| Operation | ML-DSA-65 | SLH-DSA-SHA2-256f |
|---|---|---|
| Key generation | < 1 ms | < 1 ms |
| Sign | < 5 ms | ~50 ms |
| Verify | < 2 ms | ~10 ms |
| Sign + Verify | < 10 ms (debug < 50 ms) | ~60 ms |
| 100 Sign+Verify ops | < 1 s | ~6 s |
ML-DSA-65 is the default because it offers the best balance of security, signature size, and performance. SLH-DSA-SHA2-256f is available as a conservative fallback with higher security but larger signatures.
Batch verification
Shell Chain supports parallel batch verification (feature: batch) using rayon:
// ~1.5-2× speedup on multi-core systems
batch_verifier.verify_batch(&items)?;
This is used during block import to verify all transaction signatures in parallel.
Incompatibility with ECDSA and MetaMask
Shell Chain is not compatible with MetaMask, Ledger, or other wallets that use ECDSA signatures. This is by design — ECDSA provides zero protection against quantum computers.
What doesn't work
| Tool | Why |
|---|---|
| MetaMask | Cannot generate ML-DSA-65 keys or sign PQ transactions |
| Ledger/Trezor | Hardware wallets use ECDSA/Ed25519 chips |
| ethers.js / web3.js | Client libraries assume 64-byte ECDSA signatures |
ecrecover |
ML-DSA-65 does not support public key recovery from signatures |
What to use instead
| Operation | Tool |
|---|---|
| Generate a key | shell-node key generate --output keystore.json |
| View address | shell-node key inspect keystore.json |
| Send a transaction | shell-node tx send --to 0x... --value ... --keystore keystore.json |
| Deploy a contract | shell-node tx deploy --code 0x... --keystore keystore.json |
| Call a contract | shell-node tx call --to 0x... --data 0x... |
| Check balance | shell-node account balance 0x<ADDR> |
| Check nonce | shell-node account nonce 0x<ADDR> |
| List keystores | shell-node account list --datadir shell-data |
JSON-RPC compatibility
Despite the different signature scheme, shell-chain's JSON-RPC API is Ethereum-compatible for read operations. Standard tools like curl, cast (Foundry), and custom scripts can query blocks, balances, logs, and more using the eth_ namespace. Only transaction signing requires the shell-chain CLI or SDK.
The eth_sign and eth_signTransaction methods return error -32601 because the node does not hold user private keys.
Algorithm Agility and Legacy Compatibility
SLH-DSA-SHA2-256f (Available Today)
Shell Chain already supports SLH-DSA-SHA2-256f as the fallback algorithm. It is a stateless hash-based signature scheme, providing a fundamentally different security assumption from lattice-based ML-DSA-65:
| Property | ML-DSA-65 | SLH-DSA-SHA2-256f |
|---|---|---|
| Security basis | Lattice problems (Module-LWE) | Hash function security (SHA-256) |
| PQ Security | 128-bit (NIST Level 3) | 256-bit (NIST Level 5) |
| Signature size | 3,309 bytes | ~49,856 bytes |
| Speed | Fast | Slower |
| Conservative | Moderate | Very conservative |
SLH-DSA-SHA2-256f keystores use "key_type": "slh-dsa-sha2-256f" and are managed with the same CLI tools.
Dilithium3 (Legacy Backward Compatibility)
Dilithium3 remains supported only for backward compatibility. Its wire format matches ML-DSA-65, which is why the chain can preserve byte compatibility while standardizing on the finalized NIST naming.
Hybrid Schemes (Research)
Future versions may support hybrid signature schemes that combine a classical algorithm (e.g., Ed25519) with a post-quantum algorithm (e.g., ML-DSA-65). This provides security even if one of the two algorithms is broken, offering a migration path for ecosystems transitioning from classical to post-quantum cryptography.
Algorithm Agility
Shell Chain's PQSignature container embeds the algorithm type:
pub struct PQSignature {
pub sig_type: SignatureType, // Algorithm identifier
pub data: Vec<u8>, // Raw signature bytes
}
This design enables seamless addition of new algorithms without protocol-breaking changes. The MultiVerifier dispatches to the correct verifier at runtime based on sig_type, so the network can process transactions signed with any supported algorithm in the same block.
Summary
| Component | Choice | Rationale |
|---|---|---|
| Signatures | ML-DSA-65 (default) | Fast, compact (for PQ), NIST Level 3 |
| Signatures (alt) | SLH-DSA-SHA2-256f | Conservative, hash-based, NIST Level 5 |
| Hashing | Keccak-256 | Ethereum compatibility |
| Internal hashing | BLAKE3 | Performance |
| Keystore KDF | Argon2id | Memory-hard, side-channel resistant |
| Keystore cipher | XChaCha20-Poly1305 | AEAD, safe random nonces |
| Address format | 32 bytes, blake3(algo_id || pubkey), rendered 0x + 64-hex |
PQ-bound, algo-agnostic, no truncation |
| Key zeroization | zeroize crate |
Secure memory erasure |
Shell Chain is quantum-ready today. No migration will be needed when quantum computers arrive.
Last updated: 2026-06-17