⚠️ EXPERIMENTAL PROTOCOL — Dice52-PQ is an experimental research protocol. It has not been audited, formally verified, or standardized. Do not use in production systems without independent security review.
Dice52 explores entropy-robust post-quantum ratcheting for secure channels. It is a key agreement protocol that uses hybrid KEM (ML-KEM/Kyber + X25519), ML-DSA (Dilithium) signatures to achieve quantum-resistant authenticated key exchange with classical security as a defense-in-depth.
Goal: A post-quantum, authenticated, forward-secret, per-message-key encryption protocol that preserves Dice52's Ko ordering concept.
Hybrid KEM: The shared secret remains secure provided at least one of the component KEMs (Kyber or X25519) remains secure. This follows the industry-standard approach used in TLS 1.3 hybrids, Signal PQ experiments, and OpenSSH PQ mode.
Available in:
| Aspect | Status |
|---|---|
| Specification | Draft v1 — subject to breaking changes |
| Implementation | Reference quality — not production-hardened |
| Security Audit | None — awaiting independent review |
| Formal Analysis | None — symbolic/computational proofs pending |
| Standardization | Not submitted to any standards body |
Dice52 is explicitly NOT:
| ❌ Non-Goal | Explanation |
|---|---|
| A VPN replacement | Dice52 is not WireGuard, OpenVPN, or IPsec. It does not handle routing, tunneling, or network-layer concerns. |
| A TLS replacement | Dice52 focuses on ratcheted messaging, not the full TLS handshake/record layer ecosystem. |
| A Signal replacement | While inspired by double-ratchet designs, Dice52 lacks the ecosystem, formal proofs, and battle-testing of Signal. |
| Production-ready | No security audit, no formal verification, no production deployments. |
| A complete messaging system | Dice52 is a cryptographic primitive layer, not a full application protocol. |
- Can record all traffic forever ("harvest now, decrypt later")
- Can inject, replay, and modify packets
- May eventually possess a large-scale quantum computer
- May compromise a party's device at some point (post-compromise recovery needed)
- Confidentiality
- Authentication
- Forward secrecy
- Post-compromise security
- Quantum resistance
- Deterministic but hidden key ordering (Ko)
- Per-message fresh keys
- Ko Enhancement: Independent entropy contribution from both parties (defense-in-depth)
| Replace | With |
|---|---|
| X25519 only | Hybrid KEM (ML-KEM/Kyber + X25519) |
| Ed25519 | ML-DSA (Dilithium) |
| DH ratchet | Hybrid KEM ratchet |
The hybrid KEM ensures security even if either Kyber or X25519 is broken individually.
RFC Dice52-PQ Ratchet Protocol Specification
The Ko Enhancement Phase (Section 7.1 of the RFC) provides additional security by introducing independent entropy from both parties into the ordering key derivation.
| Without Enhancement | With Enhancement |
|---|---|
| Ko derived solely from KEM shared secret | Ko includes entropy from both parties |
| Single point of failure (KEM compromise) | Defense-in-depth (need to compromise both) |
| Deterministic from handshake | Includes fresh randomness per session |
Alice (Initiator) Bob (Responder)
| |
|--- KoCommitMessage (encrypted) --->|
|<--- KoCommitMessage (encrypted) ---|
| |
|--- KoRevealMessage (encrypted) --->|
|<--- KoRevealMessage (encrypted) ---|
| |
| [verify commit, derive enhanced Ko]|
| |
|=== Session ready with enhanced Ko ==|
The enhanced Ko is computed as:
Ko = HKDF(Ko_base || R_alice || R_bob, "Dice52-Ko-Enhanced")
Paranoid mode provides additional security guarantees for high-security applications:
| Feature | Standard Mode | Paranoid Mode |
|---|---|---|
| Ko Enhancement | Once at session start | Periodic re-enhancement every N epochs |
| Messages per epoch | 33 | Configurable (1-33, default 16) |
| Ratchet frequency | Every 33 messages | More frequent |
| Latency | Lower | Higher (extra round-trips) |
| Parameter | Description | Default |
|---|---|---|
Enabled |
Activate paranoid mode | true |
KoReenhanceInterval |
Epochs between Ko re-enhancement | 10 |
MaxMessagesPerEpoch |
Messages before mandatory ratchet | 16 |
// Enable paranoid mode with default settings
alice.SetParanoidMode(dice52.DefaultParanoidConfig())
// Or customize the configuration
config := dice52.ParanoidConfig{
Enabled: true,
KoReenhanceInterval: 5, // Re-enhance Ko every 5 epochs
MaxMessagesPerEpoch: 10, // Ratchet every 10 messages
}
alice.SetParanoidMode(config)
// After ratchet, check if Ko re-enhancement is needed
if alice.NeedsKoReenhancement() {
aliceCommit, _ := alice.KoStartReenhancement()
bobCommit, _ := bob.KoStartReenhancement()
aliceReveal, _ := alice.KoProcessReenhanceCommit(bobCommit)
bobReveal, _ := bob.KoProcessReenhanceCommit(aliceCommit)
alice.KoFinalizeReenhancement(bobReveal)
bob.KoFinalizeReenhancement(aliceReveal)
}use dice52::ParanoidConfig;
// Enable paranoid mode with default settings
alice.set_paranoid_mode(ParanoidConfig::new())?;
// Or customize the configuration
let config = ParanoidConfig {
enabled: true,
ko_reenhance_interval: 5, // Re-enhance Ko every 5 epochs
max_messages_per_epoch: 10, // Ratchet every 10 messages
};
alice.set_paranoid_mode(config)?;
// After ratchet, check if Ko re-enhancement is needed
if alice.needs_ko_reenhancement() {
let alice_commit = alice.ko_start_reenhancement()?;
let bob_commit = bob.ko_start_reenhancement()?;
let alice_reveal = alice.ko_process_reenhance_commit(&bob_commit)?;
let bob_reveal = bob.ko_process_reenhance_commit(&alice_commit)?;
alice.ko_finalize_reenhancement(&bob_reveal)?;
bob.ko_finalize_reenhancement(&alice_reveal)?;
}go get github.com/dryoc/dice52Import the package in your Go code:
import "github.com/dryoc/dice52/clients/golang/pkg/dice52"package main
import (
"crypto/rand"
"fmt"
"github.com/dryoc/dice52/clients/golang/pkg/dice52"
"github.com/cloudflare/circl/kem/kyber/kyber768"
"github.com/cloudflare/circl/sign/dilithium/mode3"
)
func main() {
// Generate Kyber KEM key pairs for Alice and Bob
pubA, privA, _ := kyber768.Scheme().GenerateKeyPair()
pubB, privB, _ := kyber768.Scheme().GenerateKeyPair()
kemPubA := pubA.(*kyber768.PublicKey)
kemPrivA := privA.(*kyber768.PrivateKey)
kemPubB := pubB.(*kyber768.PublicKey)
kemPrivB := privB.(*kyber768.PrivateKey)
// Generate X25519 key pairs for Alice and Bob (hybrid KEM)
ecdhPubA, ecdhPrivA, _ := dice52.GenerateX25519Keypair()
ecdhPubB, ecdhPrivB, _ := dice52.GenerateX25519Keypair()
// Generate Dilithium identity key pairs
idPubA, idPrivA, _ := mode3.GenerateKey(rand.Reader)
idPubB, idPrivB, _ := mode3.GenerateKey(rand.Reader)
// Alice performs hybrid encapsulation to Bob's public keys (Kyber + X25519)
result, _ := dice52.InitiatorHybridEncapsulate(kemPubB, ecdhPubB)
ssAlice := result.SSHybrid
// Bob performs hybrid decapsulation using his private keys
ssBob, _ := dice52.ResponderHybridDecapsulate(kemPrivB, ecdhPrivB, result.KyberCT, result.ECDHPub)
// Derive initial keys (uses hybrid shared secret internally)
rkAlice, koAlice := dice52.DeriveInitialKeys(ssAlice)
rkBob, koBob := dice52.DeriveInitialKeys(ssBob)
// Initialize chain keys
cksAlice, ckrAlice := dice52.InitChainKeys(rkAlice, koAlice)
cksBob, ckrBob := dice52.InitChainKeys(rkBob, koBob)
// Create sessions with X25519 keys for future ratchets
alice := &dice52.Session{
SessionID: 1,
RK: rkAlice,
Ko: koAlice,
CKs: cksAlice,
CKr: ckrAlice,
KEMPub: kemPubA,
KEMPriv: kemPrivA,
ECDHPub: ecdhPubA,
ECDHPriv: ecdhPrivA,
PeerECDHPub: ecdhPubB,
IDPriv: idPrivA,
IDPub: idPubA,
PeerID: idPubB,
}
bob := &dice52.Session{
SessionID: 1,
RK: rkBob,
Ko: koBob,
CKs: ckrBob, // Bob's send = Alice's receive
CKr: cksBob, // Bob's receive = Alice's send
KEMPub: kemPubB,
KEMPriv: kemPrivB,
ECDHPub: ecdhPubB,
ECDHPriv: ecdhPrivB,
PeerECDHPub: ecdhPubA,
IDPriv: idPrivB,
IDPub: idPubB,
PeerID: idPubA,
}
// Ko Enhancement: Add independent entropy from both parties
// This provides defense-in-depth against KEM compromise
alice.SetInitiator(true)
bob.SetInitiator(false)
// Phase 1: Exchange commitments
aliceCommit, _ := alice.KoStartEnhancement(ssAlice)
bobCommit, _ := bob.KoStartEnhancement(ssBob)
// Phase 2: Exchange reveals
aliceReveal, _ := alice.KoProcessCommit(bobCommit)
bobReveal, _ := bob.KoProcessCommit(aliceCommit)
// Phase 3: Finalize enhanced Ko
alice.KoFinalize(bobReveal)
bob.KoFinalize(aliceReveal)
// Send encrypted message
msg, _ := alice.Send([]byte("Quantum-safe hello"))
pt, _ := bob.Receive(msg)
fmt.Println(string(pt)) // "Quantum-safe hello"
}make run
# or directly:
cd clients/golang && go run ./cmd/dice52make build
# or directly:
cd clients/golang && go build ./...Add to your Cargo.toml:
[dependencies]
dice52 = { git = "https://github.com/dryoc/dice52", subdirectory = "clients/rust" }Or if published to crates.io:
[dependencies]
dice52 = "0.1"use dice52::{
Session, derive_initial_keys, init_chain_keys,
generate_kem_keypair, generate_signing_keypair, generate_x25519_keypair,
initiator_hybrid_encapsulate, responder_hybrid_decapsulate,
};
fn main() {
// Generate Kyber KEM key pairs for Alice and Bob
let (kem_pub_a, kem_priv_a) = generate_kem_keypair();
let (kem_pub_b, kem_priv_b) = generate_kem_keypair();
// Generate X25519 key pairs for Alice and Bob (hybrid KEM)
let (ecdh_pub_a, ecdh_priv_a) = generate_x25519_keypair();
let (ecdh_pub_b, ecdh_priv_b) = generate_x25519_keypair();
// Generate Dilithium identity key pairs
let (id_pub_a, id_priv_a) = generate_signing_keypair();
let (id_pub_b, id_priv_b) = generate_signing_keypair();
// Alice performs hybrid encapsulation to Bob's public keys (Kyber + X25519)
let result = initiator_hybrid_encapsulate(&kem_pub_b, &ecdh_pub_b).unwrap();
let ss_alice = result.ss_hybrid.clone();
// Bob performs hybrid decapsulation using his private keys
let ss_bob = responder_hybrid_decapsulate(
&kem_priv_b,
&ecdh_priv_b,
&result.kyber_ct,
&result.ecdh_pub
).unwrap();
// Derive initial keys (uses hybrid shared secret internally)
let (rk_alice, ko_alice) = derive_initial_keys(&ss_alice);
let (rk_bob, ko_bob) = derive_initial_keys(&ss_bob);
// Initialize chain keys
let (cks_alice, ckr_alice) = init_chain_keys(&rk_alice, &ko_alice);
let (cks_bob, ckr_bob) = init_chain_keys(&rk_bob, &ko_bob);
// Create sessions with X25519 keys for future hybrid ratchets
let alice = Session::new_with_ecdh(
1,
rk_alice,
ko_alice,
cks_alice,
ckr_alice,
kem_pub_a,
kem_priv_a,
ecdh_pub_a,
ecdh_priv_a,
ecdh_pub_b.clone(), // Peer's X25519 public key
id_pub_a.clone(),
id_priv_a,
id_pub_b.clone(),
true, // Alice is initiator
);
// Bob's send = Alice's receive
let bob = Session::new_with_ecdh(
1,
rk_bob,
ko_bob,
ckr_bob, // Bob's send = Alice's receive
cks_bob, // Bob's receive = Alice's send
kem_pub_b,
kem_priv_b,
ecdh_pub_b,
ecdh_priv_b,
ecdh_pub_a, // Peer's X25519 public key
id_pub_b,
id_priv_b,
id_pub_a,
false, // Bob is responder
);
// Ko Enhancement: Add independent entropy from both parties
// This provides defense-in-depth against KEM compromise
// Phase 1: Exchange commitments
let alice_commit = alice.ko_start_enhancement(&ss_alice).unwrap();
let bob_commit = bob.ko_start_enhancement(&ss_bob).unwrap();
// Phase 2: Exchange reveals
let alice_reveal = alice.ko_process_commit(&bob_commit).unwrap();
let bob_reveal = bob.ko_process_commit(&alice_commit).unwrap();
// Phase 3: Finalize enhanced Ko
alice.ko_finalize(&bob_reveal).unwrap();
bob.ko_finalize(&alice_reveal).unwrap();
// Send encrypted message
let msg = alice.send(b"Quantum-safe hello").unwrap();
let pt = bob.receive(&msg).unwrap();
println!("{}", String::from_utf8_lossy(&pt)); // "Quantum-safe hello"
}make rust-run
# or directly:
cd clients/rust && cargo run --bin dice52-demomake rust-build
# or directly:
cd clients/rust && cargo build --releasemake rust-test
# or directly:
cd clients/rust && cargo testdice52/
├── clients/
│ ├── golang/ # Go implementation
│ │ ├── cmd/dice52/ # Go executable demo
│ │ │ └── main.go
│ │ ├── pkg/dice52/ # Go library code
│ │ │ ├── types.go # Constants & types
│ │ │ ├── kdf.go # Key derivation (HKDF)
│ │ │ ├── handshake.go # Handshake protocol
│ │ │ ├── crypto.go # ChaCha20-Poly1305 encryption
│ │ │ ├── session.go # Session management
│ │ │ └── ko_enhancement_test.go # Ko enhancement tests
│ │ ├── go.mod # Go module
│ │ └── go.sum
│ ├── rust/ # Rust implementation
│ │ ├── src/ # Rust library code
│ │ │ ├── lib.rs # Library entry point
│ │ │ ├── types.rs # Constants & types
│ │ │ ├── kdf.rs # Key derivation (HKDF)
│ │ │ ├── handshake.rs # Handshake protocol
│ │ │ ├── crypto.rs # ChaCha20-Poly1305 encryption
│ │ │ ├── session.rs # Session management
│ │ │ ├── error.rs # Error types
│ │ │ └── bin/
│ │ │ └── dice52.rs # Rust demo executable
│ │ ├── benches/
│ │ │ └── benchmarks.rs # Rust benchmarks
│ │ ├── Cargo.toml # Rust crate manifest
│ │ └── Cargo.lock
│ ├── python3/ # Python 3 implementation
│ │ ├── dice52/ # Python package
│ │ │ ├── __init__.py
│ │ │ ├── types.py # Constants & types
│ │ │ ├── kdf.py # Key derivation (HKDF)
│ │ │ ├── handshake.py # Handshake protocol
│ │ │ ├── crypto.py # ChaCha20-Poly1305 encryption
│ │ │ ├── session.py # Session management
│ │ │ └── demo.py # Demo executable
│ │ ├── tests/ # Unit tests
│ │ ├── pyproject.toml # Python package config
│ │ ├── requirements.txt # Dependencies
│ │ └── Makefile
│ └── java/ # Java implementation
│ ├── src/main/java/io/dice52/
│ │ ├── Types.java # Constants & types
│ │ ├── Kdf.java # Key derivation (HKDF)
│ │ ├── Handshake.java # Handshake protocol
│ │ ├── Crypto.java # ChaCha20-Poly1305 encryption
│ │ ├── Session.java # Session management
│ │ └── Demo.java # Demo executable
│ ├── src/test/java/ # Unit tests
│ ├── pom.xml # Maven config
│ └── Makefile
├── Makefile # Build commands for Go/Rust
├── rfc-dice52-pq-protocol.md # Protocol specification
└── README.md
| Target | Description |
|---|---|
make build |
Build Go binary |
make test |
Run Go tests |
make run |
Run Go demo |
make rust-build |
Build Rust release |
make rust-test |
Run Rust tests |
make rust-run |
Run Rust demo |
make rust-bench |
Run Rust benchmarks |
make all-build |
Build both Go and Rust |
make all-test |
Test both Go and Rust |
make all-clean |
Clean both Go and Rust |
