Advanced Topics
Ringtail Consensus
Threshold lattice-based signing for distributed agents
Ringtail Consensus
Ringtail is a threshold lattice-based signing protocol for post-quantum secure multi-party signatures.
Overview
Ringtail enables t-of-n threshold signing where:
- t parties must cooperate to produce a valid signature
- No single party can sign alone
- Signature is indistinguishable from non-threshold signature
- Post-quantum secure using lattice cryptography
Protocol Parameters
struct RingtailParams {
m @0 :UInt16 = 8; # Matrix rows
n @1 :UInt16 = 7; # Matrix columns
dbar @2 :UInt16 = 48; # Commitment dimension
kappa @3 :UInt16 = 23; # Challenge weight (Hamming weight)
phi @4 :UInt16 = 256; # Ring dimension (2^8)
keySize @5 :UInt16 = 32; # Key size in bytes (256 bits)
q @6 :UInt64 = 0x1000000004A01; # 48-bit NTT-friendly prime modulus
xi @7 :UInt32 = 30; # Rounding parameter
nu @8 :UInt32 = 29; # Rounding parameter
}Signing Protocol
Ringtail uses a two-round protocol:
Round 1: Commitment
Each party i:
- Samples random vector r_i from discrete Gaussian
- Computes commitment D_i = A * r_i
- Computes MACs for all other parties
- Broadcasts D_i with MACs
struct RingtailRound1Output {
partyId @0 :UInt32;
sessionId @1 :UInt64;
dMatrix @2 :PolyMatrix; # M x (Dbar+1) commitment matrix D_i
macs @3 :List(RingtailMac); # MACs for other participating parties
timestamp @4 :Timestamp;
}Round 2: Response
Each party i:
- Receives all D_j from other parties
- Verifies MACs
- Computes challenge c = H(D_1 || ... || D_k || message)
- Computes response z_i = r_i + c * s_i (where s_i is secret share)
- Broadcasts z_i
struct RingtailRound2Output {
partyId @0 :UInt32;
sessionId @1 :UInt64;
zShare @2 :PolyVector; # N-dimensional response vector z_i
timestamp @3 :Timestamp;
}Finalization
Combiner aggregates responses:
- Verifies each z_i against D_i
- Computes z = sum(z_i)
- Computes correction term delta
- Outputs signature (c, z, delta)
struct RingtailSignature {
c @0 :Poly; # Challenge polynomial (sparse)
z @1 :PolyVector; # Aggregated response vector
delta @2 :PolyVector; # Correction term
signers @3 :List(UInt32); # IDs of parties that contributed
sessionId @4 :UInt64; # Signing session identifier
}Party Interface
interface RingtailParty {
# Get party information
getInfo @0 () -> (info :RingtailPartyInfo);
# Execute Round 1 of signing protocol
signRound1 @1 (request :RingtailSignRequest) -> (output :RingtailRound1Output);
# Execute Round 2 of signing protocol
signRound2 @2 (
request :RingtailSignRequest,
round1Outputs :List(RingtailRound1Output)
) -> (output :RingtailRound2Output);
# Finalize signature (combiner only)
finalize @3 (
request :RingtailSignRequest,
round2Outputs :List(RingtailRound2Output)
) -> (response :RingtailSignResponse);
# Verify a signature
verify @4 (request :RingtailVerifyRequest) -> (response :RingtailVerifyResponse);
}Coordinator Interface
interface RingtailCoordinator {
# Initialize a new signing session
initSession @0 (message :Data, participants :List(UInt32)) -> (sessionId :UInt64);
# Collect Round 1 outputs from all parties
collectRound1 @1 (sessionId :UInt64) -> (outputs :List(RingtailRound1Output));
# Collect Round 2 outputs from all parties
collectRound2 @2 (sessionId :UInt64) -> (outputs :List(RingtailRound2Output));
# Get final signature for a session
getSignature @3 (sessionId :UInt64) -> (response :RingtailSignResponse);
# Cancel a signing session
cancelSession @4 (sessionId :UInt64) -> ();
# List active sessions
listSessions @5 () -> (sessions :List(UInt64));
}Usage Example
Rust
use zap_protocol::ringtail::{Party, Coordinator, SignRequest};
// Setup parties (typically on different nodes)
let party0 = Party::new(0, 3, 2)?; // Party 0 of 3, threshold 2
let party1 = Party::new(1, 3, 2)?;
let party2 = Party::new(2, 3, 2)?;
// Connect to coordinator
let coord = Coordinator::connect("zap://coordinator:9001").await?;
// Initiate signing
let session_id = coord.init_session(
b"Message to sign",
vec![0, 1, 2]
).await?;
// Round 1 (parallel on each party)
let r1_outputs = coord.collect_round1(session_id).await?;
// Round 2 (parallel on each party)
let r2_outputs = coord.collect_round2(session_id).await?;
// Get signature
let signature = coord.get_signature(session_id).await?;
// Verify
let valid = party0.verify(&signature, b"Message to sign")?;
assert!(valid);Go
import "github.com/zap-protocol/zap-go/ringtail"
// Setup
party := ringtail.NewParty(0, 3, 2) // Party 0 of 3, threshold 2
// Connect to coordinator
coord, _ := ringtail.ConnectCoordinator(ctx, "zap://coordinator:9001")
// Sign
sessionID, _ := coord.InitSession(ctx, []byte("Message to sign"), []uint32{0, 1, 2})
// Wait for signature
signature, _ := coord.GetSignature(ctx, sessionID)
// Verify
valid, _ := party.Verify(ctx, signature, []byte("Message to sign"))Party Status
enum RingtailPartyStatus {
idle @0;
signingRound1 @1;
signingRound2 @2;
combining @3;
busy @4;
offline @5;
}Error Handling
Sessions can be aborted:
struct RingtailAbort {
sessionId @0 :UInt64;
reason @1 :Text;
partyId @2 :UInt32;
}Reasons for abort:
- Timeout waiting for party responses
- MAC verification failure
- Response verification failure
- Network partition
Security Properties
- Unforgeability: Signatures cannot be forged without t parties
- Robustness: Malicious parties are detected and excluded
- PQ-Security: Based on Module-LWE hardness assumption
- Simulation Security: No party learns others' secret shares
Performance
| Operation | Time (3-of-5) |
|---|---|
| Key generation | ~100ms |
| Round 1 | ~50ms/party |
| Round 2 | ~50ms/party |
| Finalization | ~100ms |
| Total signing | ~400ms |
| Verification | ~30ms |
Use Cases
- Multi-Sig Wallets: Require multiple approvals for transactions
- Agent Coordination: Multiple agents sign consensus decisions
- Distributed Key Custody: No single point of failure
- Regulatory Compliance: Enforce multi-party authorization
Last updated on