mithril_stm/protocol/single_signature/
signature.rs

1use std::{
2    cmp::Ordering,
3    hash::{Hash, Hasher},
4};
5
6use serde::{Deserialize, Serialize};
7
8use crate::{
9    AggregateVerificationKey, LotteryIndex, MembershipDigest, Parameters, Stake, StmResult,
10    VerificationKeyForConcatenation, proof_system::SingleSignatureForConcatenation,
11    signature_scheme::BlsSignature,
12};
13
14use super::SignatureError;
15
16/// Single signature created by a single party who has won the lottery.
17/// Contains the underlying signature for the proof system and the registration index of the signer.
18#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct SingleSignature {
20    /// Underlying signature for concatenation proof system.
21    #[serde(flatten)]
22    pub(crate) concatenation_signature: SingleSignatureForConcatenation,
23    /// Merkle tree index of the signer.
24    pub signer_index: LotteryIndex,
25}
26
27impl SingleSignature {
28    /// Verify a `SingleSignature` by validating the underlying single signature for proof system.
29    ///
30    /// It only works for concatenation proof system.
31    pub fn verify<D: MembershipDigest>(
32        &self,
33        params: &Parameters,
34        pk: &VerificationKeyForConcatenation,
35        stake: &Stake,
36        avk: &AggregateVerificationKey<D>,
37        msg: &[u8],
38    ) -> StmResult<()> {
39        self.concatenation_signature.verify(
40            params,
41            pk,
42            stake,
43            avk.to_concatenation_aggregate_verification_key(),
44            msg,
45        )
46    }
47
48    /// Verify that all indices of a signature are valid.
49    pub(crate) fn check_indices(
50        &self,
51        params: &Parameters,
52        stake: &Stake,
53        msg: &[u8],
54        total_stake: &Stake,
55    ) -> StmResult<()> {
56        self.concatenation_signature
57            .check_indices(params, stake, msg, total_stake)
58    }
59
60    /// Convert a `SingleSignature` into bytes
61    ///
62    /// # Layout
63    /// * Concatenation proof system single signature bytes:
64    /// *   Length of indices
65    /// *   Indices
66    /// *   Sigma
67    /// * Merkle index of the signer.
68    pub fn to_bytes(&self) -> Vec<u8> {
69        let mut output = Vec::new();
70        let indices = self.get_concatenation_signature_indices();
71        output.extend_from_slice(&(indices.len() as u64).to_be_bytes());
72
73        for index in indices {
74            output.extend_from_slice(&index.to_be_bytes());
75        }
76
77        output.extend_from_slice(&self.get_concatenation_signature_sigma().to_bytes());
78
79        output.extend_from_slice(&self.signer_index.to_be_bytes());
80        output
81    }
82
83    /// Extract a `SingleSignature` from a byte slice.
84    pub fn from_bytes<D: MembershipDigest>(bytes: &[u8]) -> StmResult<SingleSignature> {
85        let mut u64_bytes = [0u8; 8];
86
87        u64_bytes.copy_from_slice(bytes.get(0..8).ok_or(SignatureError::SerializationError)?);
88        let nr_indexes = u64::from_be_bytes(u64_bytes) as usize;
89
90        let mut indexes = Vec::new();
91        for i in 0..nr_indexes {
92            u64_bytes.copy_from_slice(
93                bytes
94                    .get(8 + i * 8..16 + i * 8)
95                    .ok_or(SignatureError::SerializationError)?,
96            );
97            indexes.push(u64::from_be_bytes(u64_bytes));
98        }
99
100        let offset = 8 + nr_indexes * 8;
101        let sigma = BlsSignature::from_bytes(
102            bytes
103                .get(offset..offset + 48)
104                .ok_or(SignatureError::SerializationError)?,
105        )?;
106
107        u64_bytes.copy_from_slice(
108            bytes
109                .get(offset + 48..offset + 56)
110                .ok_or(SignatureError::SerializationError)?,
111        );
112        let signer_index = u64::from_be_bytes(u64_bytes);
113
114        Ok(SingleSignature {
115            concatenation_signature: SingleSignatureForConcatenation::new(sigma, indexes),
116            signer_index,
117        })
118    }
119
120    /// Get indices of the single signature for concatenation proof system.
121    pub fn get_concatenation_signature_indices(&self) -> Vec<LotteryIndex> {
122        self.concatenation_signature.get_indices().to_vec()
123    }
124
125    /// Get underlying BLS signature of the concatenation single signature.
126    pub fn get_concatenation_signature_sigma(&self) -> BlsSignature {
127        self.concatenation_signature.get_sigma()
128    }
129
130    /// Set the indices of the underlying single signature for proof system.
131    pub fn set_concatenation_signature_indices(&mut self, indices: &[LotteryIndex]) {
132        self.concatenation_signature.set_indices(indices)
133    }
134}
135
136impl Hash for SingleSignature {
137    fn hash<H: Hasher>(&self, state: &mut H) {
138        Hash::hash_slice(&self.concatenation_signature.get_sigma().to_bytes(), state)
139    }
140}
141
142impl PartialEq for SingleSignature {
143    fn eq(&self, other: &Self) -> bool {
144        self.concatenation_signature == other.concatenation_signature
145    }
146}
147
148impl Eq for SingleSignature {}
149
150impl PartialOrd for SingleSignature {
151    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
152        Some(std::cmp::Ord::cmp(self, other))
153    }
154}
155
156impl Ord for SingleSignature {
157    fn cmp(&self, other: &Self) -> Ordering {
158        self.signer_index.cmp(&other.signer_index)
159    }
160}
161
162#[cfg(test)]
163mod tests {
164
165    use rand_chacha::ChaCha20Rng;
166    use rand_core::SeedableRng;
167
168    use super::SignatureError;
169    use crate::{
170        AggregateVerificationKey, BlsSignatureError, Clerk, KeyRegistration,
171        MithrilMembershipDigest, Parameters, RegistrationEntry, Signer, SingleSignature,
172        VerificationKeyProofOfPossessionForConcatenation, proof_system::ConcatenationProofSigner,
173        signature_scheme::BlsSigningKey,
174    };
175
176    type D = MithrilMembershipDigest;
177
178    const TEST_MESSAGE: [u8; 16] = [42u8; 16];
179
180    fn test_parameters() -> Parameters {
181        Parameters {
182            m: 10,
183            k: 5,
184            phi_f: 0.8,
185        }
186    }
187
188    struct SingleSignatureTestContext {
189        signer_1: Signer<D>,
190        vk_1: VerificationKeyProofOfPossessionForConcatenation,
191        vk_2: VerificationKeyProofOfPossessionForConcatenation,
192        avk: AggregateVerificationKey<D>,
193    }
194
195    fn build_single_signature_context(
196        number_of_signers: usize,
197        rng_seed: [u8; 32],
198    ) -> SingleSignatureTestContext {
199        assert!(
200            number_of_signers >= 2,
201            "at least 2 signers are required for these tests"
202        );
203
204        let mut rng = ChaCha20Rng::from_seed(rng_seed);
205        let params = test_parameters();
206
207        let mut signing_keys = Vec::with_capacity(number_of_signers);
208        let mut verification_keys = Vec::with_capacity(number_of_signers);
209        for _ in 0..number_of_signers {
210            let signing_key = BlsSigningKey::generate(&mut rng);
211            let verification_key =
212                VerificationKeyProofOfPossessionForConcatenation::from(&signing_key);
213            signing_keys.push(signing_key);
214            verification_keys.push(verification_key);
215        }
216
217        let mut registration = KeyRegistration::initialize();
218        for verification_key in &verification_keys {
219            let entry = RegistrationEntry::new(
220                *verification_key,
221                1,
222                #[cfg(feature = "future_snark")]
223                None,
224            )
225            .unwrap();
226            registration.register_by_entry(&entry).unwrap();
227        }
228
229        let closed_key_registration = registration.close_registration();
230        let mut signing_keys = signing_keys.into_iter();
231        let sk_1 = signing_keys.next().expect("at least one signer exists");
232        let mut verification_keys = verification_keys.into_iter();
233        let vk_1 = verification_keys.next().expect(
234            "internal test setup invariant violated: missing first verification key (vk_1)",
235        );
236        let vk_2 = verification_keys.next().expect(
237            "internal test setup invariant violated: missing second verification key (vk_2)",
238        );
239        let signer_1: Signer<D> = Signer::new(
240            1,
241            ConcatenationProofSigner::new(
242                1,
243                2,
244                params,
245                sk_1,
246                vk_1.vk,
247                closed_key_registration.clone().to_merkle_tree(),
248            ),
249            closed_key_registration,
250            params,
251            1,
252        );
253
254        let clerk = Clerk::new_clerk_from_signer(&signer_1);
255        let avk = clerk.compute_aggregate_verification_key();
256
257        SingleSignatureTestContext {
258            signer_1,
259            vk_1,
260            vk_2,
261            avk,
262        }
263    }
264
265    mod golden {
266        use super::*;
267
268        type D = MithrilMembershipDigest;
269
270        const GOLDEN_BYTES: &[u8; 96] = &[
271            0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0,
272            0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 8, 149, 157, 201, 187, 140, 54, 0, 128, 209, 88, 16, 203,
273            61, 78, 77, 98, 161, 133, 58, 152, 29, 74, 217, 113, 64, 100, 10, 161, 186, 167, 133,
274            114, 211, 153, 218, 56, 223, 84, 105, 242, 41, 54, 224, 170, 208, 185, 126, 83, 0, 0,
275            0, 0, 0, 0, 0, 1,
276        ];
277
278        fn golden_value() -> SingleSignature {
279            let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
280            let message = [0u8; 16];
281            let params = Parameters {
282                m: 10,
283                k: 5,
284                phi_f: 0.8,
285            };
286            let sk_1 = BlsSigningKey::generate(&mut rng);
287            let sk_2 = BlsSigningKey::generate(&mut rng);
288            let pk_1 = VerificationKeyProofOfPossessionForConcatenation::from(&sk_1);
289            let pk_2 = VerificationKeyProofOfPossessionForConcatenation::from(&sk_2);
290
291            let mut registration = KeyRegistration::initialize();
292            let entry1 = RegistrationEntry::new(
293                pk_1,
294                1,
295                #[cfg(feature = "future_snark")]
296                None,
297            )
298            .unwrap();
299            let entry2 = RegistrationEntry::new(
300                pk_2,
301                1,
302                #[cfg(feature = "future_snark")]
303                None,
304            )
305            .unwrap();
306            registration.register_by_entry(&entry1).unwrap();
307            registration.register_by_entry(&entry2).unwrap();
308            let closed_key_registration = registration.close_registration();
309
310            let signer: Signer<MithrilMembershipDigest> = Signer::new(
311                1,
312                ConcatenationProofSigner::new(
313                    1,
314                    2,
315                    params,
316                    sk_1,
317                    pk_1.vk,
318                    closed_key_registration.to_merkle_tree(),
319                ),
320                closed_key_registration,
321                params,
322                1,
323            );
324            signer.create_single_signature(&message).unwrap()
325        }
326
327        #[test]
328        fn golden_conversions() {
329            let value = SingleSignature::from_bytes::<D>(GOLDEN_BYTES)
330                .expect("This from bytes should not fail");
331            assert_eq!(golden_value(), value);
332
333            let serialized = SingleSignature::to_bytes(&value);
334            let golden_serialized = SingleSignature::to_bytes(&golden_value());
335            assert_eq!(golden_serialized, serialized);
336        }
337    }
338
339    mod golden_json {
340        use super::*;
341
342        const GOLDEN_JSON: &str = r#"
343        {
344            "sigma": [
345                149, 157, 201, 187, 140, 54, 0, 128, 209, 88, 16, 203, 61, 78, 77, 98, 161,
346                133, 58, 152, 29, 74, 217, 113, 64, 100, 10, 161, 186, 167, 133, 114, 211,
347                153, 218, 56, 223, 84, 105, 242, 41, 54, 224, 170, 208, 185, 126, 83
348            ],
349            "indexes": [1, 4, 5, 8],
350            "signer_index": 1
351        }"#;
352
353        fn golden_value() -> SingleSignature {
354            let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
355            let message = [0u8; 16];
356            let params = Parameters {
357                m: 10,
358                k: 5,
359                phi_f: 0.8,
360            };
361            let sk_1 = BlsSigningKey::generate(&mut rng);
362            let sk_2 = BlsSigningKey::generate(&mut rng);
363            let pk_1 = VerificationKeyProofOfPossessionForConcatenation::from(&sk_1);
364            let pk_2 = VerificationKeyProofOfPossessionForConcatenation::from(&sk_2);
365
366            let mut registration = KeyRegistration::initialize();
367            let entry1 = RegistrationEntry::new(
368                pk_1,
369                1,
370                #[cfg(feature = "future_snark")]
371                None,
372            )
373            .unwrap();
374            let entry2 = RegistrationEntry::new(
375                pk_2,
376                1,
377                #[cfg(feature = "future_snark")]
378                None,
379            )
380            .unwrap();
381            registration.register_by_entry(&entry1).unwrap();
382            registration.register_by_entry(&entry2).unwrap();
383
384            let closed_key_registration = registration.close_registration();
385
386            let signer: Signer<MithrilMembershipDigest> = Signer::new(
387                1,
388                ConcatenationProofSigner::new(
389                    1,
390                    2,
391                    params,
392                    sk_1,
393                    pk_1.vk,
394                    closed_key_registration.to_merkle_tree(),
395                ),
396                closed_key_registration.clone(),
397                params,
398                1,
399            );
400            signer.create_single_signature(&message).unwrap()
401        }
402
403        #[test]
404        fn golden_conversions() {
405            let value = serde_json::from_str(GOLDEN_JSON)
406                .expect("This JSON deserialization should not fail");
407            assert_eq!(golden_value(), value);
408
409            let serialized =
410                serde_json::to_string(&value).expect("This JSON serialization should not fail");
411            let golden_serialized = serde_json::to_string(&golden_value())
412                .expect("This JSON serialization should not fail");
413            assert_eq!(golden_serialized, serialized);
414        }
415    }
416
417    #[test]
418    fn verify_fails_with_wrong_verification_key() {
419        let ctx = build_single_signature_context(2, [0u8; 32]);
420        let signature = ctx
421            .signer_1
422            .create_single_signature(&TEST_MESSAGE)
423            .expect("signature should be created");
424
425        let params = test_parameters();
426        let error = signature
427            .verify(&params, &ctx.vk_2.vk, &1, &ctx.avk, &TEST_MESSAGE)
428            .expect_err("Verification should fail with wrong verification key");
429        assert!(
430            matches!(
431                error.downcast_ref::<BlsSignatureError>(),
432                Some(BlsSignatureError::SignatureInvalid(_))
433            ),
434            "Unexpected error variant: {error:?}"
435        );
436    }
437
438    #[test]
439    fn verify_fails_with_out_of_bounds_index() {
440        let ctx = build_single_signature_context(2, [0u8; 32]);
441        let mut signature = ctx
442            .signer_1
443            .create_single_signature(&TEST_MESSAGE)
444            .expect("signature should be created");
445
446        let params = test_parameters();
447        signature.set_concatenation_signature_indices(&[params.m + 1]);
448
449        let error = signature
450            .verify(&params, &ctx.vk_1.vk, &1, &ctx.avk, &TEST_MESSAGE)
451            .expect_err("Verification should fail with invalid index");
452        assert!(
453            matches!(
454                error.downcast_ref::<SignatureError>(),
455                Some(SignatureError::IndexBoundFailed(_, _))
456            ),
457            "Unexpected error variant: {error:?}"
458        );
459    }
460
461    #[test]
462    fn verify_fails_with_wrong_message() {
463        let ctx = build_single_signature_context(2, [0u8; 32]);
464        let signature = ctx
465            .signer_1
466            .create_single_signature(&TEST_MESSAGE)
467            .expect("signature should be created");
468        let wrong_message = [43u8; 16];
469
470        let params = test_parameters();
471        let error = signature
472            .verify(&params, &ctx.vk_1.vk, &1, &ctx.avk, &wrong_message)
473            .expect_err("Verification should fail with wrong message");
474        assert!(
475            matches!(
476                error.downcast_ref::<BlsSignatureError>(),
477                Some(BlsSignatureError::SignatureInvalid(_))
478            ),
479            "Unexpected error variant: {error:?}"
480        );
481    }
482
483    #[test]
484    fn verify_fails_with_different_registration_avk() {
485        let signing_ctx = build_single_signature_context(2, [0u8; 32]);
486        let different_registration_ctx = build_single_signature_context(3, [0u8; 32]);
487        let signature = signing_ctx
488            .signer_1
489            .create_single_signature(&TEST_MESSAGE)
490            .expect("signature should be created");
491
492        let params = test_parameters();
493        let error = signature
494            .verify(
495                &params,
496                &signing_ctx.vk_1.vk,
497                &1,
498                &different_registration_ctx.avk,
499                &TEST_MESSAGE,
500            )
501            .expect_err("Verification should fail with a different registration AVK");
502        assert!(
503            matches!(
504                error.downcast_ref::<BlsSignatureError>(),
505                Some(BlsSignatureError::SignatureInvalid(_))
506            ),
507            "Unexpected error variant: {error:?}"
508        );
509    }
510}