mithril_stm/protocol/single_signature/
signature.rs1use std::{
2 cmp::Ordering,
3 hash::{Hash, Hasher},
4};
5
6use anyhow::{Context, anyhow};
7use serde::{Deserialize, Serialize};
8
9use crate::{
10 AggregateVerificationKey, Index, MembershipDigest, Parameters, Stake, StmResult,
11 VerificationKey, is_lottery_won, signature_scheme::BlsSignature,
12};
13
14use super::SignatureError;
15
16#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct SingleSignature {
19 pub sigma: BlsSignature,
21 pub indexes: Vec<Index>,
23 pub signer_index: Index,
25}
26
27impl SingleSignature {
28 pub fn verify<D: MembershipDigest>(
31 &self,
32 params: &Parameters,
33 pk: &VerificationKey,
34 stake: &Stake,
35 avk: &AggregateVerificationKey<D>,
36 msg: &[u8],
37 ) -> StmResult<()> {
38 let msgp = avk.get_merkle_tree_batch_commitment().concatenate_with_message(msg);
39 self.sigma.verify(&msgp, pk).with_context(|| {
40 format!(
41 "Single signature verification failed for signer index {}.",
42 self.signer_index
43 )
44 })?;
45 self.check_indices(params, stake, &msgp, &avk.get_total_stake())
46 .with_context(|| {
47 format!(
48 "Single signature verification failed for signer index {}.",
49 self.signer_index
50 )
51 })?;
52 Ok(())
53 }
54
55 pub(crate) fn check_indices(
57 &self,
58 params: &Parameters,
59 stake: &Stake,
60 msg: &[u8],
61 total_stake: &Stake,
62 ) -> StmResult<()> {
63 for &index in &self.indexes {
64 if index > params.m {
65 return Err(anyhow!(SignatureError::IndexBoundFailed(index, params.m)));
66 }
67
68 let ev = self.sigma.evaluate_dense_mapping(msg, index);
69
70 if !is_lottery_won(params.phi_f, ev, *stake, *total_stake) {
71 return Err(anyhow!(SignatureError::LotteryLost));
72 }
73 }
74
75 Ok(())
76 }
77
78 pub fn to_bytes(&self) -> Vec<u8> {
88 let mut output = Vec::new();
89 output.extend_from_slice(&(self.indexes.len() as u64).to_be_bytes());
90
91 for index in &self.indexes {
92 output.extend_from_slice(&index.to_be_bytes());
93 }
94
95 output.extend_from_slice(&self.sigma.to_bytes());
96
97 output.extend_from_slice(&self.signer_index.to_be_bytes());
98 output
99 }
100
101 pub fn from_bytes<D: MembershipDigest>(bytes: &[u8]) -> StmResult<SingleSignature> {
103 let mut u64_bytes = [0u8; 8];
104
105 u64_bytes.copy_from_slice(bytes.get(0..8).ok_or(SignatureError::SerializationError)?);
106 let nr_indexes = u64::from_be_bytes(u64_bytes) as usize;
107
108 let mut indexes = Vec::new();
109 for i in 0..nr_indexes {
110 u64_bytes.copy_from_slice(
111 bytes
112 .get(8 + i * 8..16 + i * 8)
113 .ok_or(SignatureError::SerializationError)?,
114 );
115 indexes.push(u64::from_be_bytes(u64_bytes));
116 }
117
118 let offset = 8 + nr_indexes * 8;
119 let sigma = BlsSignature::from_bytes(
120 bytes
121 .get(offset..offset + 48)
122 .ok_or(SignatureError::SerializationError)?,
123 )?;
124
125 u64_bytes.copy_from_slice(
126 bytes
127 .get(offset + 48..offset + 56)
128 .ok_or(SignatureError::SerializationError)?,
129 );
130 let signer_index = u64::from_be_bytes(u64_bytes);
131
132 Ok(SingleSignature {
133 sigma,
134 indexes,
135 signer_index,
136 })
137 }
138
139 fn compare_signer_index(&self, other: &Self) -> Ordering {
141 self.signer_index.cmp(&other.signer_index)
142 }
143
144 #[deprecated(since = "0.5.0", note = "This function will be removed")]
146 pub fn cmp_stm_sig(&self, other: &Self) -> Ordering {
147 Self::compare_signer_index(self, other)
148 }
149}
150
151impl Hash for SingleSignature {
152 fn hash<H: Hasher>(&self, state: &mut H) {
153 Hash::hash_slice(&self.sigma.to_bytes(), state)
154 }
155}
156
157impl PartialEq for SingleSignature {
158 fn eq(&self, other: &Self) -> bool {
159 self.sigma == other.sigma
160 }
161}
162
163impl Eq for SingleSignature {}
164
165impl PartialOrd for SingleSignature {
166 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
167 Some(std::cmp::Ord::cmp(self, other))
168 }
169}
170
171impl Ord for SingleSignature {
172 fn cmp(&self, other: &Self) -> Ordering {
173 self.signer_index.cmp(&other.signer_index)
174 }
175}
176
177#[cfg(test)]
178mod tests {
179 mod golden {
180 use rand_chacha::ChaCha20Rng;
181 use rand_core::SeedableRng;
182
183 use crate::{
184 ClosedKeyRegistration, KeyRegistration, MithrilMembershipDigest, Parameters, Signer,
185 SingleSignature,
186 signature_scheme::{BlsSigningKey, BlsVerificationKeyProofOfPossession},
187 };
188
189 const GOLDEN_JSON: &str = r#"
190 {
191 "sigma": [
192 149, 157, 201, 187, 140, 54, 0, 128, 209, 88, 16, 203, 61, 78, 77, 98, 161,
193 133, 58, 152, 29, 74, 217, 113, 64, 100, 10, 161, 186, 167, 133, 114, 211,
194 153, 218, 56, 223, 84, 105, 242, 41, 54, 224, 170, 208, 185, 126, 83
195 ],
196 "indexes": [1, 4, 5, 8],
197 "signer_index": 1
198 }"#;
199
200 fn golden_value() -> SingleSignature {
201 let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
202 let msg = [0u8; 16];
203 let params = Parameters {
204 m: 10,
205 k: 5,
206 phi_f: 0.8,
207 };
208 let sk_1 = BlsSigningKey::generate(&mut rng);
209 let sk_2 = BlsSigningKey::generate(&mut rng);
210 let pk_1 = BlsVerificationKeyProofOfPossession::from(&sk_1);
211 let pk_2 = BlsVerificationKeyProofOfPossession::from(&sk_2);
212 let mut key_reg = KeyRegistration::init();
213 key_reg.register(1, pk_1).unwrap();
214 key_reg.register(1, pk_2).unwrap();
215 let closed_key_reg: ClosedKeyRegistration<MithrilMembershipDigest> = key_reg.close();
216 let signer = Signer::set_signer(1, 1, params, sk_1, pk_1.vk, closed_key_reg);
217 signer.sign(&msg).unwrap()
218 }
219
220 #[test]
221 fn golden_conversions() {
222 let value = serde_json::from_str(GOLDEN_JSON)
223 .expect("This JSON deserialization should not fail");
224 assert_eq!(golden_value(), value);
225
226 let serialized =
227 serde_json::to_string(&value).expect("This JSON serialization should not fail");
228 let golden_serialized = serde_json::to_string(&golden_value())
229 .expect("This JSON serialization should not fail");
230 assert_eq!(golden_serialized, serialized);
231 }
232 }
233}