mithril_stm/signature_scheme/bls_multi_signature/
signature.rs

1use std::{cmp::Ordering, iter::Sum};
2
3use anyhow::{Context, anyhow};
4use blake2::{Blake2b, Blake2b512, Digest};
5use blst::{
6    blst_p1, blst_p2,
7    min_sig::{AggregateSignature, PublicKey as BlstVk, Signature as BlstSig},
8    p1_affines, p2_affines,
9};
10use digest::consts::U16;
11
12use crate::{Index, StmResult};
13
14use super::{
15    BlsSignatureError, BlsVerificationKey, blst_error_to_stm_error,
16    helper::unsafe_helpers::{p1_affine_to_sig, p2_affine_to_vk, sig_to_p1, vk_from_p2_affine},
17};
18
19/// MultiSig signature, which is a wrapper over the `BlstSig` type.
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub struct BlsSignature(pub BlstSig);
22
23impl BlsSignature {
24    /// Verify a signature against a verification key.
25    pub fn verify(&self, msg: &[u8], mvk: &BlsVerificationKey) -> StmResult<()> {
26        blst_error_to_stm_error(
27            self.0.validate(true).map_or_else(
28                |e| e,
29                |_| {
30                    self.0
31                        .verify(false, msg, &[], &[], &mvk.to_blst_verification_key(), false)
32                },
33            ),
34            Some(*self),
35            None,
36        )
37    }
38
39    /// Dense mapping function indexed by the index to be evaluated.
40    /// We hash the signature to produce a 64 bytes integer.
41    /// The return value of this function refers to
42    /// `ev = H("map" || msg || index || σ) <- MSP.Eval(msg,index,σ)` given in paper.
43    pub(crate) fn evaluate_dense_mapping(&self, msg: &[u8], index: Index) -> [u8; 64] {
44        let hasher = Blake2b512::new()
45            .chain_update(b"map")
46            .chain_update(msg)
47            .chain_update(index.to_le_bytes())
48            .chain_update(self.to_bytes())
49            .finalize();
50
51        let mut output = [0u8; 64];
52        output.copy_from_slice(&hasher);
53
54        output
55    }
56
57    /// Convert an `Signature` to its compressed byte representation.
58    pub fn to_bytes(self) -> [u8; 48] {
59        self.0.to_bytes()
60    }
61
62    /// Convert a string of bytes into a `MspSig`.
63    ///
64    /// # Error
65    /// Returns an error if the byte string does not represent a point in the curve.
66    pub fn from_bytes(bytes: &[u8]) -> StmResult<Self> {
67        let bytes = bytes.get(..48).ok_or(BlsSignatureError::SerializationError)?;
68        match BlstSig::sig_validate(bytes, true) {
69            Ok(sig) => Ok(Self(sig)),
70            Err(e) => Err(blst_error_to_stm_error(e, None, None)
71                .expect_err("If deserialization is not successful, blst returns and error different to SUCCESS."))
72        }
73    }
74
75    /// Compare two signatures. Used for PartialOrd impl, used to rank signatures. The comparison
76    /// function can be anything, as long as it is consistent across different nodes.
77    fn compare_signatures(&self, other: &Self) -> Ordering {
78        let self_bytes = self.to_bytes();
79        let other_bytes = other.to_bytes();
80        let mut result = Ordering::Equal;
81
82        for (i, j) in self_bytes.iter().zip(other_bytes.iter()) {
83            result = i.cmp(j);
84            if result != Ordering::Equal {
85                return result;
86            }
87        }
88        result
89    }
90
91    /// Aggregate a slice of verification keys and Signatures by first hashing the
92    /// signatures into random scalars, and multiplying the signature and verification
93    /// key with the resulting value. This follows the steps defined in Figure 6,
94    /// `Aggregate` step.
95    pub fn aggregate(
96        vks: &[BlsVerificationKey],
97        sigs: &[BlsSignature],
98    ) -> StmResult<(BlsVerificationKey, BlsSignature)> {
99        if vks.len() != sigs.len() || vks.is_empty() {
100            return Err(anyhow!(BlsSignatureError::AggregateSignatureInvalid));
101        }
102
103        if vks.len() < 2 {
104            return Ok((vks[0], sigs[0]));
105        }
106
107        let mut hashed_sigs = Blake2b::<U16>::new();
108        for sig in sigs {
109            hashed_sigs.update(sig.to_bytes());
110        }
111
112        // First we generate the scalars
113        let mut scalars = Vec::with_capacity(vks.len() * 128);
114        let mut signatures = Vec::with_capacity(vks.len());
115        for (index, sig) in sigs.iter().enumerate() {
116            let mut hasher = hashed_sigs.clone();
117            hasher.update(index.to_be_bytes());
118            signatures.push(sig.0);
119            scalars.extend_from_slice(&hasher.finalize());
120        }
121
122        let transmuted_vks: Vec<blst_p2> = vks.iter().map(vk_from_p2_affine).collect();
123        let transmuted_sigs: Vec<blst_p1> = signatures.iter().map(sig_to_p1).collect();
124
125        let grouped_vks = p2_affines::from(transmuted_vks.as_slice());
126        let grouped_sigs = p1_affines::from(transmuted_sigs.as_slice());
127
128        let aggr_vk: BlstVk = p2_affine_to_vk(&grouped_vks.mult(&scalars, 128));
129        let aggr_sig: BlstSig = p1_affine_to_sig(&grouped_sigs.mult(&scalars, 128));
130
131        Ok((BlsVerificationKey(aggr_vk), BlsSignature(aggr_sig)))
132    }
133
134    /// Verify a set of signatures with their corresponding verification keys using the
135    /// aggregation mechanism of Figure 6.
136    pub fn verify_aggregate(
137        msg: &[u8],
138        vks: &[BlsVerificationKey],
139        sigs: &[BlsSignature],
140    ) -> StmResult<()> {
141        let (aggr_vk, aggr_sig) = Self::aggregate(vks, sigs).with_context(|| "Multi signature verification failed in aggregation of verification keys and signatures.")?;
142
143        blst_error_to_stm_error(
144            aggr_sig.0.verify(
145                false,
146                msg,
147                &[],
148                &[],
149                &aggr_vk.to_blst_verification_key(),
150                false,
151            ),
152            Some(aggr_sig),
153            None,
154        )
155    }
156
157    /// Batch verify several sets of signatures with their corresponding verification keys.
158    pub fn batch_verify_aggregates(
159        msgs: &[Vec<u8>],
160        vks: &[BlsVerificationKey],
161        sigs: &[BlsSignature],
162    ) -> StmResult<()> {
163        let batched_sig: BlstSig = match AggregateSignature::aggregate(
164            &(sigs.iter().map(|sig| &sig.0).collect::<Vec<&BlstSig>>()),
165            false,
166        ) {
167            Ok(sig) => BlstSig::from_aggregate(&sig),
168            Err(e) => return blst_error_to_stm_error(e, None, None),
169        };
170
171        let p2_vks: Vec<BlstVk> = vks.iter().map(|vk| vk.to_blst_verification_key()).collect();
172        let p2_vks_ref: Vec<&BlstVk> = p2_vks.iter().collect();
173        let slice_msgs = msgs.iter().map(|msg| msg.as_slice()).collect::<Vec<&[u8]>>();
174
175        blst_error_to_stm_error(
176            batched_sig.aggregate_verify(false, &slice_msgs, &[], &p2_vks_ref, false),
177            None,
178            None,
179        )
180        .map_err(|_| anyhow!(BlsSignatureError::BatchInvalid))
181    }
182}
183
184impl<'a> Sum<&'a Self> for BlsSignature {
185    fn sum<I>(iter: I) -> Self
186    where
187        I: Iterator<Item = &'a Self>,
188    {
189        let signatures: Vec<&BlstSig> = iter.map(|x| &x.0).collect();
190        assert!(!signatures.is_empty(), "One cannot add an empty vector");
191        let aggregate = AggregateSignature::aggregate(&signatures, false)
192            .expect("An MspSig is always a valid signature. This function only fails if signatures is empty or if the signatures are invalid, none of which can happen.")
193            .to_signature();
194
195        Self(aggregate)
196    }
197}
198
199impl PartialOrd for BlsSignature {
200    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
201        Some(std::cmp::Ord::cmp(self, other))
202    }
203}
204
205impl Ord for BlsSignature {
206    fn cmp(&self, other: &Self) -> Ordering {
207        self.compare_signatures(other)
208    }
209}
210
211#[cfg(test)]
212mod tests {
213    mod golden {
214
215        use rand_chacha::ChaCha20Rng;
216        use rand_core::SeedableRng;
217
218        use crate::signature_scheme::{BlsSignature, BlsSigningKey};
219
220        const GOLDEN_JSON: &str = r#"[132,95,124,197,185,105,193,171,114,182,52,171,205,119,202,188,2,213,61,125,219,242,10,131,53,219,53,197,157,42,152,194,234,161,244,204,2,134,47,179,176,49,200,232,120,241,180,246]"#;
221
222        fn golden_value() -> BlsSignature {
223            let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
224            let sk = BlsSigningKey::generate(&mut rng);
225            let msg = [0u8; 32];
226            sk.sign(&msg)
227        }
228
229        #[test]
230        fn golden_conversions() {
231            let value = serde_json::from_str(GOLDEN_JSON)
232                .expect("This JSON deserialization should not fail");
233            assert_eq!(golden_value(), value);
234
235            let serialized =
236                serde_json::to_string(&value).expect("This JSON serialization should not fail");
237            let golden_serialized = serde_json::to_string(&golden_value())
238                .expect("This JSON serialization should not fail");
239            assert_eq!(golden_serialized, serialized);
240        }
241    }
242}