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#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct SingleSignature {
20 #[serde(flatten)]
22 pub(crate) concatenation_signature: SingleSignatureForConcatenation,
23 pub signer_index: LotteryIndex,
25}
26
27impl SingleSignature {
28 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 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 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 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 pub fn get_concatenation_signature_indices(&self) -> Vec<LotteryIndex> {
122 self.concatenation_signature.get_indices().to_vec()
123 }
124
125 pub fn get_concatenation_signature_sigma(&self) -> BlsSignature {
127 self.concatenation_signature.get_sigma()
128 }
129
130 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}