commonware_consensus/simplex/scheme/mod.rs
1//! Signing scheme implementations for `simplex`.
2//!
3//! # Attributable Schemes and Fault Evidence
4//!
5//! Signing schemes differ in whether per-validator activities can be used as evidence of either
6//! liveness or of committing a fault:
7//!
8//! - **Attributable Schemes** ([`ed25519`], [`bls12381_multisig`], [`secp256r1`]): Individual signatures can be
9//! presented to some third party as evidence of either liveness or of committing a fault. Certificates contain signer
10//! indices alongside individual signatures, enabling secure per-validator activity tracking and conflict detection.
11//!
12//! - **Non-Attributable schemes** ([`bls12381_threshold`]): Individual signatures cannot be presented
13//! to some third party as evidence of either liveness or of committing a fault because they can be forged
14//! by other players (often after some quorum of partial signatures are collected). With [`bls12381_threshold`],
15//! possession of any `t` valid partial signatures can be used to forge a partial signature for any other player.
16//! Because peer connections are authenticated, evidence can be used locally (as it must be sent by said participant)
17//! but can't be used by an external observer.
18//!
19//! The [`CertificateScheme::is_attributable()`] associated function signals whether evidence can be safely
20//! exposed. For applications only interested in collecting evidence for liveness/faults, use [`reporter::AttributableReporter`]
21//! which automatically handles filtering and verification based on scheme (hiding votes/proofs that are not attributable). To
22//! observe all exported activity, process all messages passed through the [`crate::Reporter`] interface (note that reports
23//! are not exhaustive: votes below the activity window are dropped without being reported, see
24//! [`crate::simplex::types::Activity`]).
25//!
26//! # BLS12-381 Threshold Variants
27//!
28//! The [`bls12381_threshold`] module provides two variants:
29//!
30//! - [`bls12381_threshold::standard`]: Standard threshold signatures. Certificates contain only a
31//! vote signature recovered from partial signatures.
32//!
33//! - [`bls12381_threshold::vrf`]: Threshold VRF (Verifiable Random Function) that produces both
34//! vote signatures and per-round seed signatures. The seed can be used for randomness (e.g.,
35//! leader election, timelock encryption).
36//!
37//! **Security Warning for VRF Usage**: Each round's seed partial signs only the round and is
38//! carried by every vote type, so the `f` Byzantine participants of a `3f+1` committee can recover
39//! the seed once any `f+1` honest participants have voted (even for conflicting proposals), before
40//! the round resolves and without any certificate forming. A malicious leader can use this early
41//! knowledge to front-run the outcome. Applications should employ a "commit-then-reveal" pattern
42//! by binding randomness requests in finalized blocks **before** the reveal occurs (e.g.,
43//! `draw(view+100)`). See [`bls12381_threshold::vrf`] for details.
44
45use crate::simplex::types::Subject;
46use bytes::Bytes;
47use commonware_codec::Encode;
48use commonware_cryptography::{
49 Digest,
50 certificate::{
51 Namespace as CertificateNamespace, Scheme as CertificateScheme,
52 Subject as CertificateSubject, Verifier,
53 },
54};
55use commonware_utils::{N3f1, union};
56
57pub mod bls12381_multisig;
58pub mod bls12381_threshold;
59pub mod ed25519;
60commonware_macros::stability_mod!(ALPHA, pub mod secp256r1);
61
62#[cfg(not(target_arch = "wasm32"))]
63pub mod reporter;
64
65/// Pre-computed namespaces for simplex voting subjects.
66///
67/// This struct holds the pre-computed namespace bytes for each vote type.
68#[derive(Clone, Debug)]
69pub struct Namespace {
70 /// Namespace for notarize votes/certificates.
71 pub notarize: Vec<u8>,
72 /// Namespace for nullify votes/certificates.
73 pub nullify: Vec<u8>,
74 /// Namespace for finalize votes/certificates.
75 pub finalize: Vec<u8>,
76 /// Namespace for seed signatures (used by threshold schemes).
77 pub seed: Vec<u8>,
78}
79
80impl Namespace {
81 /// Creates a new SimplexNamespace from a base namespace.
82 pub fn new(namespace: &[u8]) -> Self {
83 Self {
84 notarize: notarize_namespace(namespace),
85 nullify: nullify_namespace(namespace),
86 finalize: finalize_namespace(namespace),
87 seed: seed_namespace(namespace),
88 }
89 }
90}
91
92impl CertificateNamespace for Namespace {
93 fn derive(namespace: &[u8]) -> Self {
94 Self::new(namespace)
95 }
96}
97
98impl<'a, D: Digest> CertificateSubject for Subject<'a, D> {
99 type Namespace = Namespace;
100
101 fn namespace<'b>(&self, derived: &'b Self::Namespace) -> &'b [u8] {
102 match self {
103 Self::Notarize { .. } => &derived.notarize,
104 Self::Nullify { .. } => &derived.nullify,
105 Self::Finalize { .. } => &derived.finalize,
106 }
107 }
108
109 fn message(&self) -> Bytes {
110 match self {
111 Self::Notarize { proposal } => proposal.encode(),
112 Self::Nullify { round } => round.encode(),
113 Self::Finalize { proposal } => proposal.encode(),
114 }
115 }
116}
117
118/// Marker trait for certificate verifiers compatible with `simplex`.
119///
120/// This trait binds a [`Verifier`] to the [`Subject`] subject type and [`N3f1`]
121/// fault model used by the simplex protocol. It is automatically implemented
122/// for any compatible verifier.
123pub trait CertificateVerifier<D: Digest>:
124 for<'a> Verifier<Subject<'a, D> = Subject<'a, D>, Faults = N3f1>
125{
126}
127
128impl<D: Digest, S> CertificateVerifier<D> for S where
129 S: for<'a> Verifier<Subject<'a, D> = Subject<'a, D>, Faults = N3f1>
130{
131}
132
133/// Marker trait for signing schemes compatible with `simplex`.
134///
135/// This trait binds a [`CertificateScheme`] to the [`Subject`] subject type and
136/// [`N3f1`] fault model used by the simplex protocol. It is automatically
137/// implemented for any compatible scheme.
138pub trait Scheme<D: Digest>:
139 CertificateVerifier<D> + for<'a> CertificateScheme<Subject<'a, D> = Subject<'a, D>>
140{
141}
142
143impl<D: Digest, S> Scheme<D> for S where
144 S: CertificateVerifier<D> + for<'a> CertificateScheme<Subject<'a, D> = Subject<'a, D>>
145{
146}
147
148// Constants for domain separation in signature verification
149// These are used to prevent cross-protocol attacks and message-type confusion
150const SEED_SUFFIX: &[u8] = b"_SEED";
151const NOTARIZE_SUFFIX: &[u8] = b"_NOTARIZE";
152const NULLIFY_SUFFIX: &[u8] = b"_NULLIFY";
153const FINALIZE_SUFFIX: &[u8] = b"_FINALIZE";
154
155/// Creates a namespace for seed messages by appending the SEED_SUFFIX
156/// The seed is used for leader election and randomness generation
157#[inline]
158pub(crate) fn seed_namespace(namespace: &[u8]) -> Vec<u8> {
159 union(namespace, SEED_SUFFIX)
160}
161
162/// Creates a namespace for notarize messages by appending the NOTARIZE_SUFFIX
163/// Domain separation prevents cross-protocol attacks
164#[inline]
165pub(crate) fn notarize_namespace(namespace: &[u8]) -> Vec<u8> {
166 union(namespace, NOTARIZE_SUFFIX)
167}
168
169/// Creates a namespace for nullify messages by appending the NULLIFY_SUFFIX
170/// Domain separation prevents cross-protocol attacks
171#[inline]
172pub(crate) fn nullify_namespace(namespace: &[u8]) -> Vec<u8> {
173 union(namespace, NULLIFY_SUFFIX)
174}
175
176/// Creates a namespace for finalize messages by appending the FINALIZE_SUFFIX
177/// Domain separation prevents cross-protocol attacks
178#[inline]
179pub(crate) fn finalize_namespace(namespace: &[u8]) -> Vec<u8> {
180 union(namespace, FINALIZE_SUFFIX)
181}