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 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(¶ms, &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(¶ms, &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(¶ms, &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 ¶ms,
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}