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 crate::{
169        KeyRegistration, MithrilMembershipDigest, Parameters, RegistrationEntry, Signer,
170        SingleSignature, VerificationKeyProofOfPossessionForConcatenation,
171        proof_system::ConcatenationProofSigner, signature_scheme::BlsSigningKey,
172    };
173
174    mod golden {
175        use super::*;
176
177        type D = MithrilMembershipDigest;
178
179        const GOLDEN_BYTES: &[u8; 96] = &[
180            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,
181            0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 8, 149, 157, 201, 187, 140, 54, 0, 128, 209, 88, 16, 203,
182            61, 78, 77, 98, 161, 133, 58, 152, 29, 74, 217, 113, 64, 100, 10, 161, 186, 167, 133,
183            114, 211, 153, 218, 56, 223, 84, 105, 242, 41, 54, 224, 170, 208, 185, 126, 83, 0, 0,
184            0, 0, 0, 0, 0, 1,
185        ];
186
187        fn golden_value() -> SingleSignature {
188            let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
189            let message = [0u8; 16];
190            let params = Parameters {
191                m: 10,
192                k: 5,
193                phi_f: 0.8,
194            };
195            let sk_1 = BlsSigningKey::generate(&mut rng);
196            let sk_2 = BlsSigningKey::generate(&mut rng);
197            let pk_1 = VerificationKeyProofOfPossessionForConcatenation::from(&sk_1);
198            let pk_2 = VerificationKeyProofOfPossessionForConcatenation::from(&sk_2);
199
200            let mut registration = KeyRegistration::initialize();
201            let entry1 = RegistrationEntry::new(pk_1, 1).unwrap();
202            let entry2 = RegistrationEntry::new(pk_2, 1).unwrap();
203            registration.register_by_entry(&entry1).unwrap();
204            registration.register_by_entry(&entry2).unwrap();
205            let closed_key_registration = registration.close_registration();
206
207            let signer: Signer<MithrilMembershipDigest> = Signer::new(
208                1,
209                ConcatenationProofSigner::new(
210                    1,
211                    2,
212                    params,
213                    sk_1,
214                    pk_1.vk,
215                    closed_key_registration.clone().key_registration.into_merkle_tree(),
216                ),
217                closed_key_registration,
218                params,
219                1,
220            );
221            signer.create_single_signature(&message).unwrap()
222        }
223
224        #[test]
225        fn golden_conversions() {
226            let value = SingleSignature::from_bytes::<D>(GOLDEN_BYTES)
227                .expect("This from bytes should not fail");
228            assert_eq!(golden_value(), value);
229
230            let serialized = SingleSignature::to_bytes(&value);
231            let golden_serialized = SingleSignature::to_bytes(&golden_value());
232            assert_eq!(golden_serialized, serialized);
233        }
234    }
235
236    mod golden_json {
237        use super::*;
238
239        const GOLDEN_JSON: &str = r#"
240        {
241            "sigma": [
242                149, 157, 201, 187, 140, 54, 0, 128, 209, 88, 16, 203, 61, 78, 77, 98, 161,
243                133, 58, 152, 29, 74, 217, 113, 64, 100, 10, 161, 186, 167, 133, 114, 211,
244                153, 218, 56, 223, 84, 105, 242, 41, 54, 224, 170, 208, 185, 126, 83
245            ],
246            "indexes": [1, 4, 5, 8],
247            "signer_index": 1
248        }"#;
249
250        fn golden_value() -> SingleSignature {
251            let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
252            let message = [0u8; 16];
253            let params = Parameters {
254                m: 10,
255                k: 5,
256                phi_f: 0.8,
257            };
258            let sk_1 = BlsSigningKey::generate(&mut rng);
259            let sk_2 = BlsSigningKey::generate(&mut rng);
260            let pk_1 = VerificationKeyProofOfPossessionForConcatenation::from(&sk_1);
261            let pk_2 = VerificationKeyProofOfPossessionForConcatenation::from(&sk_2);
262
263            let mut registration = KeyRegistration::initialize();
264            let entry1 = RegistrationEntry::new(pk_1, 1).unwrap();
265            let entry2 = RegistrationEntry::new(pk_2, 1).unwrap();
266            registration.register_by_entry(&entry1).unwrap();
267            registration.register_by_entry(&entry2).unwrap();
268
269            let closed_key_registration = registration.close_registration();
270
271            let signer: Signer<MithrilMembershipDigest> = Signer::new(
272                1,
273                ConcatenationProofSigner::new(
274                    1,
275                    2,
276                    params,
277                    sk_1,
278                    pk_1.vk,
279                    closed_key_registration.clone().key_registration.into_merkle_tree(),
280                ),
281                closed_key_registration.clone(),
282                params,
283                1,
284            );
285            signer.create_single_signature(&message).unwrap()
286        }
287
288        #[test]
289        fn golden_conversions() {
290            let value = serde_json::from_str(GOLDEN_JSON)
291                .expect("This JSON deserialization should not fail");
292            assert_eq!(golden_value(), value);
293
294            let serialized =
295                serde_json::to_string(&value).expect("This JSON serialization should not fail");
296            let golden_serialized = serde_json::to_string(&golden_value())
297                .expect("This JSON serialization should not fail");
298            assert_eq!(golden_serialized, serialized);
299        }
300    }
301}