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). If
22//! full observability is desired, process all messages passed through the [`crate::Reporter`] interface.
23//!
24//! # BLS12-381 Threshold Variants
25//!
26//! The [`bls12381_threshold`] module provides two variants:
27//!
28//! - [`bls12381_threshold::standard`]: Standard threshold signatures. Certificates contain only a
29//! vote signature recovered from partial signatures.
30//!
31//! - [`bls12381_threshold::vrf`]: Threshold VRF (Verifiable Random Function) that produces both
32//! vote signatures and per-round seed signatures. The seed can be used for randomness (e.g.,
33//! leader election, timelock encryption).
34//!
35//! **Security Warning for VRF Usage**: It is **not safe** to use a round's randomness to drive
36//! execution in that same round. A malicious leader can selectively distribute blocks to gain
37//! early visibility of the randomness output, then choose nullification if the outcome is
38//! unfavorable. Applications should employ a "commit-then-reveal" pattern by binding randomness
39//! requests in finalized blocks **before** the reveal occurs (e.g., `draw(view+100)`).
40
41use crate::simplex::types::Subject;
42use bytes::Bytes;
43use commonware_codec::Encode;
44use commonware_cryptography::{
45 certificate::{
46 Namespace as CertificateNamespace, Scheme as CertificateScheme,
47 Subject as CertificateSubject, Verifier,
48 },
49 Digest,
50};
51use commonware_utils::union;
52
53pub mod bls12381_multisig;
54pub mod bls12381_threshold;
55pub mod ed25519;
56commonware_macros::stability_mod!(ALPHA, pub mod secp256r1);
57
58#[cfg(not(target_arch = "wasm32"))]
59pub mod reporter;
60
61/// Pre-computed namespaces for simplex voting subjects.
62///
63/// This struct holds the pre-computed namespace bytes for each vote type.
64#[derive(Clone, Debug)]
65pub struct Namespace {
66 /// Namespace for notarize votes/certificates.
67 pub notarize: Vec<u8>,
68 /// Namespace for nullify votes/certificates.
69 pub nullify: Vec<u8>,
70 /// Namespace for finalize votes/certificates.
71 pub finalize: Vec<u8>,
72 /// Namespace for seed signatures (used by threshold schemes).
73 pub seed: Vec<u8>,
74}
75
76impl Namespace {
77 /// Creates a new SimplexNamespace from a base namespace.
78 pub fn new(namespace: &[u8]) -> Self {
79 Self {
80 notarize: notarize_namespace(namespace),
81 nullify: nullify_namespace(namespace),
82 finalize: finalize_namespace(namespace),
83 seed: seed_namespace(namespace),
84 }
85 }
86}
87
88impl CertificateNamespace for Namespace {
89 fn derive(namespace: &[u8]) -> Self {
90 Self::new(namespace)
91 }
92}
93
94impl<'a, D: Digest> CertificateSubject for Subject<'a, D> {
95 type Namespace = Namespace;
96
97 fn namespace<'b>(&self, derived: &'b Self::Namespace) -> &'b [u8] {
98 match self {
99 Self::Notarize { .. } => &derived.notarize,
100 Self::Nullify { .. } => &derived.nullify,
101 Self::Finalize { .. } => &derived.finalize,
102 }
103 }
104
105 fn message(&self) -> Bytes {
106 match self {
107 Self::Notarize { proposal } => proposal.encode(),
108 Self::Nullify { round } => round.encode(),
109 Self::Finalize { proposal } => proposal.encode(),
110 }
111 }
112}
113
114/// Marker trait for certificate verifiers compatible with `simplex`.
115///
116/// This trait binds a [`Verifier`] to the [`Subject`] subject type
117/// used by the simplex protocol. It is automatically implemented for any verifier whose subject
118/// type matches `Subject<'a, D>`.
119pub trait CertificateVerifier<D: Digest>:
120 for<'a> Verifier<Subject<'a, D> = Subject<'a, D>>
121{
122}
123
124impl<D: Digest, S> CertificateVerifier<D> for S where
125 S: for<'a> Verifier<Subject<'a, D> = Subject<'a, D>>
126{
127}
128
129/// Marker trait for signing schemes compatible with `simplex`.
130///
131/// This trait binds a [`CertificateScheme`] to the [`Subject`] subject type
132/// used by the simplex protocol. It is automatically implemented for any scheme
133/// whose subject type matches `Subject<'a, D>`.
134pub trait Scheme<D: Digest>:
135 CertificateVerifier<D> + for<'a> CertificateScheme<Subject<'a, D> = Subject<'a, D>>
136{
137}
138
139impl<D: Digest, S> Scheme<D> for S where
140 S: for<'a> CertificateScheme<Subject<'a, D> = Subject<'a, D>>
141{
142}
143
144// Constants for domain separation in signature verification
145// These are used to prevent cross-protocol attacks and message-type confusion
146const SEED_SUFFIX: &[u8] = b"_SEED";
147const NOTARIZE_SUFFIX: &[u8] = b"_NOTARIZE";
148const NULLIFY_SUFFIX: &[u8] = b"_NULLIFY";
149const FINALIZE_SUFFIX: &[u8] = b"_FINALIZE";
150
151/// Creates a namespace for seed messages by appending the SEED_SUFFIX
152/// The seed is used for leader election and randomness generation
153#[inline]
154pub(crate) fn seed_namespace(namespace: &[u8]) -> Vec<u8> {
155 union(namespace, SEED_SUFFIX)
156}
157
158/// Creates a namespace for notarize messages by appending the NOTARIZE_SUFFIX
159/// Domain separation prevents cross-protocol attacks
160#[inline]
161pub(crate) fn notarize_namespace(namespace: &[u8]) -> Vec<u8> {
162 union(namespace, NOTARIZE_SUFFIX)
163}
164
165/// Creates a namespace for nullify messages by appending the NULLIFY_SUFFIX
166/// Domain separation prevents cross-protocol attacks
167#[inline]
168pub(crate) fn nullify_namespace(namespace: &[u8]) -> Vec<u8> {
169 union(namespace, NULLIFY_SUFFIX)
170}
171
172/// Creates a namespace for finalize messages by appending the FINALIZE_SUFFIX
173/// Domain separation prevents cross-protocol attacks
174#[inline]
175pub(crate) fn finalize_namespace(namespace: &[u8]) -> Vec<u8> {
176 union(namespace, FINALIZE_SUFFIX)
177}