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, Index, MembershipDigest, Parameters, Stake, StmResult,
10    VerificationKey, proof_system::SingleSignatureForConcatenation, signature_scheme::BlsSignature,
11};
12
13use super::SignatureError;
14
15/// Single signature created by a single party who has won the lottery.
16/// Contains the underlying signature for the proof system and the registration index of the signer.
17#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct SingleSignature {
19    /// Underlying signature for concatenation proof system.
20    #[serde(flatten)]
21    pub(crate) concatenation_signature: SingleSignatureForConcatenation,
22    /// Merkle tree index of the signer.
23    pub signer_index: Index,
24}
25
26impl SingleSignature {
27    /// Verify a `SingleSignature` by validating the underlying single signature for proof system.
28    pub fn verify<D: MembershipDigest>(
29        &self,
30        params: &Parameters,
31        pk: &VerificationKey,
32        stake: &Stake,
33        avk: &AggregateVerificationKey<D>,
34        msg: &[u8],
35    ) -> StmResult<()> {
36        self.concatenation_signature.verify(params, pk, stake, avk, msg)
37    }
38
39    /// Verify that all indices of a signature are valid.
40    pub(crate) fn check_indices(
41        &self,
42        params: &Parameters,
43        stake: &Stake,
44        msg: &[u8],
45        total_stake: &Stake,
46    ) -> StmResult<()> {
47        self.concatenation_signature
48            .check_indices(params, stake, msg, total_stake)
49    }
50
51    /// Convert a `SingleSignature` into bytes
52    ///
53    /// # Layout
54    /// * Concatenation proof system single signature bytes:
55    /// *   Length of indices
56    /// *   Indices
57    /// *   Sigma
58    /// * Merkle index of the signer.
59    pub fn to_bytes(&self) -> Vec<u8> {
60        let mut output = Vec::new();
61        let indices = self.get_concatenation_signature_indices();
62        output.extend_from_slice(&(indices.len() as u64).to_be_bytes());
63
64        for index in indices {
65            output.extend_from_slice(&index.to_be_bytes());
66        }
67
68        output.extend_from_slice(&self.get_concatenation_signature_sigma().to_bytes());
69
70        output.extend_from_slice(&self.signer_index.to_be_bytes());
71        output
72    }
73
74    /// Extract a `SingleSignature` from a byte slice.
75    pub fn from_bytes<D: MembershipDigest>(bytes: &[u8]) -> StmResult<SingleSignature> {
76        let mut u64_bytes = [0u8; 8];
77
78        u64_bytes.copy_from_slice(bytes.get(0..8).ok_or(SignatureError::SerializationError)?);
79        let nr_indexes = u64::from_be_bytes(u64_bytes) as usize;
80
81        let mut indexes = Vec::new();
82        for i in 0..nr_indexes {
83            u64_bytes.copy_from_slice(
84                bytes
85                    .get(8 + i * 8..16 + i * 8)
86                    .ok_or(SignatureError::SerializationError)?,
87            );
88            indexes.push(u64::from_be_bytes(u64_bytes));
89        }
90
91        let offset = 8 + nr_indexes * 8;
92        let sigma = BlsSignature::from_bytes(
93            bytes
94                .get(offset..offset + 48)
95                .ok_or(SignatureError::SerializationError)?,
96        )?;
97
98        u64_bytes.copy_from_slice(
99            bytes
100                .get(offset + 48..offset + 56)
101                .ok_or(SignatureError::SerializationError)?,
102        );
103        let signer_index = u64::from_be_bytes(u64_bytes);
104
105        Ok(SingleSignature {
106            concatenation_signature: SingleSignatureForConcatenation::new(sigma, indexes),
107            signer_index,
108        })
109    }
110
111    /// Get indices of the single signature for concatenation proof system.
112    pub fn get_concatenation_signature_indices(&self) -> Vec<Index> {
113        self.concatenation_signature.get_indices().to_vec()
114    }
115
116    /// Get underlying BLS signature of the concatenation single signature.
117    pub fn get_concatenation_signature_sigma(&self) -> BlsSignature {
118        self.concatenation_signature.get_sigma()
119    }
120
121    /// Set the indices of the underlying single signature for proof system.
122    pub fn set_concatenation_signature_indices(&mut self, indices: &[Index]) {
123        self.concatenation_signature.set_indices(indices)
124    }
125}
126
127impl Hash for SingleSignature {
128    fn hash<H: Hasher>(&self, state: &mut H) {
129        Hash::hash_slice(&self.concatenation_signature.get_sigma().to_bytes(), state)
130    }
131}
132
133impl PartialEq for SingleSignature {
134    fn eq(&self, other: &Self) -> bool {
135        self.concatenation_signature == other.concatenation_signature
136    }
137}
138
139impl Eq for SingleSignature {}
140
141impl PartialOrd for SingleSignature {
142    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
143        Some(std::cmp::Ord::cmp(self, other))
144    }
145}
146
147impl Ord for SingleSignature {
148    fn cmp(&self, other: &Self) -> Ordering {
149        self.signer_index.cmp(&other.signer_index)
150    }
151}
152
153#[cfg(test)]
154mod tests {
155
156    use rand_chacha::ChaCha20Rng;
157    use rand_core::SeedableRng;
158
159    use crate::{
160        ClosedKeyRegistration, KeyRegistration, MithrilMembershipDigest, Parameters, Signer,
161        SingleSignature,
162        signature_scheme::{BlsSigningKey, BlsVerificationKeyProofOfPossession},
163    };
164
165    mod golden {
166        use super::*;
167
168        type D = MithrilMembershipDigest;
169
170        const GOLDEN_BYTES: &[u8; 96] = &[
171            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,
172            0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 8, 149, 157, 201, 187, 140, 54, 0, 128, 209, 88, 16, 203,
173            61, 78, 77, 98, 161, 133, 58, 152, 29, 74, 217, 113, 64, 100, 10, 161, 186, 167, 133,
174            114, 211, 153, 218, 56, 223, 84, 105, 242, 41, 54, 224, 170, 208, 185, 126, 83, 0, 0,
175            0, 0, 0, 0, 0, 1,
176        ];
177
178        fn golden_value() -> SingleSignature {
179            let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
180            let msg = [0u8; 16];
181            let params = Parameters {
182                m: 10,
183                k: 5,
184                phi_f: 0.8,
185            };
186            let sk_1 = BlsSigningKey::generate(&mut rng);
187            let sk_2 = BlsSigningKey::generate(&mut rng);
188            let pk_1 = BlsVerificationKeyProofOfPossession::from(&sk_1);
189            let pk_2 = BlsVerificationKeyProofOfPossession::from(&sk_2);
190            let mut key_reg = KeyRegistration::init();
191            key_reg.register(1, pk_1).unwrap();
192            key_reg.register(1, pk_2).unwrap();
193            let closed_key_reg: ClosedKeyRegistration<MithrilMembershipDigest> = key_reg.close();
194            let signer = Signer::set_signer(1, 1, params, sk_1, pk_1.vk, closed_key_reg);
195            signer.sign(&msg).unwrap()
196        }
197
198        #[test]
199        fn golden_conversions() {
200            let value = SingleSignature::from_bytes::<D>(GOLDEN_BYTES)
201                .expect("This from bytes should not fail");
202            assert_eq!(golden_value(), value);
203
204            let serialized = SingleSignature::to_bytes(&value);
205            let golden_serialized = SingleSignature::to_bytes(&golden_value());
206            assert_eq!(golden_serialized, serialized);
207        }
208    }
209
210    mod golden_json {
211        use super::*;
212
213        const GOLDEN_JSON: &str = r#"
214        {
215            "sigma": [
216                149, 157, 201, 187, 140, 54, 0, 128, 209, 88, 16, 203, 61, 78, 77, 98, 161,
217                133, 58, 152, 29, 74, 217, 113, 64, 100, 10, 161, 186, 167, 133, 114, 211,
218                153, 218, 56, 223, 84, 105, 242, 41, 54, 224, 170, 208, 185, 126, 83
219            ],
220            "indexes": [1, 4, 5, 8],
221            "signer_index": 1
222        }"#;
223
224        fn golden_value() -> SingleSignature {
225            let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
226            let msg = [0u8; 16];
227            let params = Parameters {
228                m: 10,
229                k: 5,
230                phi_f: 0.8,
231            };
232            let sk_1 = BlsSigningKey::generate(&mut rng);
233            let sk_2 = BlsSigningKey::generate(&mut rng);
234            let pk_1 = BlsVerificationKeyProofOfPossession::from(&sk_1);
235            let pk_2 = BlsVerificationKeyProofOfPossession::from(&sk_2);
236            let mut key_reg = KeyRegistration::init();
237            key_reg.register(1, pk_1).unwrap();
238            key_reg.register(1, pk_2).unwrap();
239            let closed_key_reg: ClosedKeyRegistration<MithrilMembershipDigest> = key_reg.close();
240            let signer = Signer::set_signer(1, 1, params, sk_1, pk_1.vk, closed_key_reg);
241            signer.sign(&msg).unwrap()
242        }
243
244        #[test]
245        fn golden_conversions() {
246            let value = serde_json::from_str(GOLDEN_JSON)
247                .expect("This JSON deserialization should not fail");
248            assert_eq!(golden_value(), value);
249
250            let serialized =
251                serde_json::to_string(&value).expect("This JSON serialization should not fail");
252            let golden_serialized = serde_json::to_string(&golden_value())
253                .expect("This JSON serialization should not fail");
254            assert_eq!(golden_serialized, serialized);
255        }
256    }
257}