1#![no_std]
2#![allow(non_snake_case)]
3#![allow(clippy::derive_partial_eq_without_eq)]
5#![deny(missing_docs)]
6#![forbid(unsafe_code)]
7#![deny(clippy::indexing_slicing)]
8#![deny(clippy::unwrap_used)]
9#![cfg_attr(docsrs, feature(doc_auto_cfg))]
10#![cfg_attr(docsrs, feature(doc_cfg))]
11#![doc = include_str!("../README.md")]
12#![doc = document_features::document_features!()]
13
14#[macro_use]
15extern crate alloc;
16
17use core::marker::PhantomData;
18
19use alloc::{
20 collections::{BTreeMap, BTreeSet},
21 fmt::{self, Debug},
22 vec::Vec,
23};
24
25use derive_getters::Getters;
26#[cfg(any(test, feature = "test-impl"))]
27use hex::FromHex;
28use keys::PublicKeyPackage;
29use rand_core::{CryptoRng, RngCore};
30use serialization::SerializableScalar;
31use zeroize::Zeroize;
32
33pub mod batch;
34#[cfg(any(test, feature = "test-impl"))]
35pub mod benches;
36mod error;
37mod identifier;
38pub mod keys;
39pub mod round1;
40pub mod round2;
41mod scalar_mul;
42pub mod serialization;
46mod signature;
47mod signing_key;
48#[cfg(any(test, feature = "test-impl"))]
49pub mod tests;
50mod traits;
51mod verifying_key;
52
53pub use error::{Error, FieldError, GroupError};
54pub use identifier::Identifier;
55use scalar_mul::VartimeMultiscalarMul;
56#[cfg(feature = "serde")]
58pub use serde;
59pub use signature::Signature;
60pub use signing_key::SigningKey;
61pub use traits::{Ciphersuite, Element, Field, Group, Scalar};
62pub use verifying_key::VerifyingKey;
63
64#[derive(Copy, Clone)]
68pub struct Challenge<C: Ciphersuite>(pub(crate) <<C::Group as Group>::Field as Field>::Scalar);
69
70impl<C> Challenge<C>
71where
72 C: Ciphersuite,
73{
74 #[cfg_attr(feature = "internals", visibility::make(pub))]
76 #[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
77 #[allow(dead_code)]
78 pub(crate) fn from_scalar(
79 scalar: <<<C as Ciphersuite>::Group as Group>::Field as Field>::Scalar,
80 ) -> Self {
81 Self(scalar)
82 }
83
84 #[cfg_attr(feature = "internals", visibility::make(pub))]
86 #[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
87 pub(crate) fn to_scalar(
88 self,
89 ) -> <<<C as Ciphersuite>::Group as Group>::Field as Field>::Scalar {
90 self.0
91 }
92}
93
94impl<C> Debug for Challenge<C>
95where
96 C: Ciphersuite,
97{
98 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
99 f.debug_tuple("Secret")
100 .field(&hex::encode(<<C::Group as Group>::Field>::serialize(
101 &self.0,
102 )))
103 .finish()
104 }
105}
106
107#[cfg_attr(feature = "internals", visibility::make(pub))]
117#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
118fn challenge<C>(
119 R: &Element<C>,
120 verifying_key: &VerifyingKey<C>,
121 msg: &[u8],
122) -> Result<Challenge<C>, Error<C>>
123where
124 C: Ciphersuite,
125{
126 let mut preimage = Vec::new();
127
128 preimage.extend_from_slice(<C::Group>::serialize(R)?.as_ref());
129 preimage.extend_from_slice(<C::Group>::serialize(&verifying_key.to_element())?.as_ref());
130 preimage.extend_from_slice(msg);
131
132 Ok(Challenge(C::H2(&preimage[..])))
133}
134
135#[cfg_attr(feature = "internals", visibility::make(pub))]
139#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
140pub(crate) fn random_nonzero<C: Ciphersuite, R: RngCore + CryptoRng>(rng: &mut R) -> Scalar<C> {
141 loop {
142 let scalar = <<C::Group as Group>::Field>::random(rng);
143
144 if scalar != <<C::Group as Group>::Field>::zero() {
145 return scalar;
146 }
147 }
148}
149
150#[derive(Copy, Clone, Debug, PartialEq, Eq, Zeroize)]
151#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
152#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
153struct Header<C: Ciphersuite> {
154 #[cfg_attr(
156 feature = "serde",
157 serde(deserialize_with = "crate::serialization::version_deserialize::<_>")
158 )]
159 version: u8,
160 #[cfg_attr(
162 feature = "serde",
163 serde(serialize_with = "crate::serialization::ciphersuite_serialize::<_, C>")
164 )]
165 #[cfg_attr(
166 feature = "serde",
167 serde(deserialize_with = "crate::serialization::ciphersuite_deserialize::<_, C>")
168 )]
169 ciphersuite: (),
170 #[cfg_attr(feature = "serde", serde(skip))]
171 phantom: PhantomData<C>,
172}
173
174impl<C> Default for Header<C>
175where
176 C: Ciphersuite,
177{
178 fn default() -> Self {
179 Self {
180 version: Default::default(),
181 ciphersuite: Default::default(),
182 phantom: Default::default(),
183 }
184 }
185}
186
187#[derive(Clone, PartialEq, Eq)]
194pub struct BindingFactor<C: Ciphersuite>(Scalar<C>);
195
196impl<C> BindingFactor<C>
197where
198 C: Ciphersuite,
199{
200 pub fn serialize(&self) -> Vec<u8> {
202 SerializableScalar::<C>(self.0).serialize()
203 }
204}
205
206impl<C> Debug for BindingFactor<C>
207where
208 C: Ciphersuite,
209{
210 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
211 f.debug_tuple("BindingFactor")
212 .field(&hex::encode(self.serialize()))
213 .finish()
214 }
215}
216
217#[derive(Clone)]
219#[cfg_attr(feature = "internals", visibility::make(pub))]
220#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
221pub(crate) struct BindingFactorList<C: Ciphersuite>(BTreeMap<Identifier<C>, BindingFactor<C>>);
222
223impl<C> BindingFactorList<C>
224where
225 C: Ciphersuite,
226{
227 #[cfg(feature = "internals")]
229 pub fn new(binding_factors: BTreeMap<Identifier<C>, BindingFactor<C>>) -> Self {
230 Self(binding_factors)
231 }
232
233 pub fn get(&self, key: &Identifier<C>) -> Option<&BindingFactor<C>> {
235 self.0.get(key)
236 }
237}
238
239#[cfg_attr(feature = "internals", visibility::make(pub))]
243#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
244pub(crate) fn compute_binding_factor_list<C>(
245 signing_package: &SigningPackage<C>,
246 verifying_key: &VerifyingKey<C>,
247 additional_prefix: &[u8],
248) -> Result<BindingFactorList<C>, Error<C>>
249where
250 C: Ciphersuite,
251{
252 let preimages = signing_package.binding_factor_preimages(verifying_key, additional_prefix)?;
253
254 Ok(BindingFactorList(
255 preimages
256 .iter()
257 .map(|(identifier, preimage)| {
258 let binding_factor = C::H1(preimage);
259 (*identifier, BindingFactor(binding_factor))
260 })
261 .collect(),
262 ))
263}
264
265#[cfg(any(test, feature = "test-impl"))]
266impl<C> FromHex for BindingFactor<C>
267where
268 C: Ciphersuite,
269{
270 type Error = &'static str;
271
272 fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
273 let v: Vec<u8> = FromHex::from_hex(hex).map_err(|_| "invalid hex")?;
274
275 match v.try_into() {
276 Ok(bytes) => <<C::Group as Group>::Field>::deserialize(&bytes)
277 .map(|scalar| Self(scalar))
278 .map_err(|_| "malformed scalar encoding"),
279 Err(_) => Err("malformed scalar encoding"),
280 }
281 }
282}
283
284#[cfg_attr(feature = "internals", visibility::make(pub))]
296#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
297fn compute_lagrange_coefficient<C: Ciphersuite>(
298 x_set: &BTreeSet<Identifier<C>>,
299 x: Option<Identifier<C>>,
300 x_i: Identifier<C>,
301) -> Result<Scalar<C>, Error<C>> {
302 if x_set.is_empty() {
303 return Err(Error::IncorrectNumberOfIdentifiers);
304 }
305 let mut num = <<C::Group as Group>::Field>::one();
306 let mut den = <<C::Group as Group>::Field>::one();
307
308 let mut x_i_found = false;
309
310 for x_j in x_set.iter() {
311 if x_i == *x_j {
312 x_i_found = true;
313 continue;
314 }
315
316 if let Some(x) = x {
317 num = num * (x.to_scalar() - x_j.to_scalar());
318 den = den * (x_i.to_scalar() - x_j.to_scalar());
319 } else {
320 num = num * x_j.to_scalar();
322 den = den * (x_j.to_scalar() - x_i.to_scalar());
323 }
324 }
325 if !x_i_found {
326 return Err(Error::UnknownIdentifier);
327 }
328
329 Ok(
330 num * <<C::Group as Group>::Field>::invert(&den)
331 .map_err(|_| Error::DuplicatedIdentifier)?,
332 )
333}
334
335#[cfg_attr(feature = "internals", visibility::make(pub))]
341#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
342fn derive_interpolating_value<C: Ciphersuite>(
343 signer_id: &Identifier<C>,
344 signing_package: &SigningPackage<C>,
345) -> Result<Scalar<C>, Error<C>> {
346 compute_lagrange_coefficient(
347 &signing_package
348 .signing_commitments()
349 .keys()
350 .cloned()
351 .collect(),
352 None,
353 *signer_id,
354 )
355}
356
357#[derive(Clone, Debug, PartialEq, Eq, Getters)]
360#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
361#[cfg_attr(feature = "serde", serde(bound = "C: Ciphersuite"))]
362#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
363pub struct SigningPackage<C: Ciphersuite> {
364 #[getter(skip)]
366 pub(crate) header: Header<C>,
367 signing_commitments: BTreeMap<Identifier<C>, round1::SigningCommitments<C>>,
370 #[cfg_attr(
375 feature = "serde",
376 serde(
377 serialize_with = "serdect::slice::serialize_hex_lower_or_bin",
378 deserialize_with = "serdect::slice::deserialize_hex_or_bin_vec"
379 )
380 )]
381 message: Vec<u8>,
382}
383
384impl<C> SigningPackage<C>
385where
386 C: Ciphersuite,
387{
388 pub fn new(
392 signing_commitments: BTreeMap<Identifier<C>, round1::SigningCommitments<C>>,
393 message: &[u8],
394 ) -> SigningPackage<C> {
395 SigningPackage {
396 header: Header::default(),
397 signing_commitments,
398 message: message.to_vec(),
399 }
400 }
401
402 pub fn signing_commitment(
404 &self,
405 identifier: &Identifier<C>,
406 ) -> Option<round1::SigningCommitments<C>> {
407 self.signing_commitments.get(identifier).copied()
408 }
409
410 #[cfg_attr(feature = "internals", visibility::make(pub))]
413 #[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
414 #[allow(clippy::type_complexity)]
415 pub fn binding_factor_preimages(
416 &self,
417 verifying_key: &VerifyingKey<C>,
418 additional_prefix: &[u8],
419 ) -> Result<Vec<(Identifier<C>, Vec<u8>)>, Error<C>> {
420 let mut binding_factor_input_prefix = Vec::new();
421
422 binding_factor_input_prefix.extend_from_slice(verifying_key.serialize()?.as_ref());
426
427 binding_factor_input_prefix.extend_from_slice(C::H4(self.message.as_slice()).as_ref());
431 binding_factor_input_prefix.extend_from_slice(
432 C::H5(&round1::encode_group_commitments(self.signing_commitments())?[..]).as_ref(),
433 );
434 binding_factor_input_prefix.extend_from_slice(additional_prefix);
435
436 Ok(self
437 .signing_commitments()
438 .keys()
439 .map(|identifier| {
440 let mut binding_factor_input = Vec::new();
441
442 binding_factor_input.extend_from_slice(&binding_factor_input_prefix);
443 binding_factor_input.extend_from_slice(identifier.serialize().as_ref());
444 (*identifier, binding_factor_input)
445 })
446 .collect())
447 }
448}
449
450#[cfg(feature = "serialization")]
451impl<C> SigningPackage<C>
452where
453 C: Ciphersuite,
454{
455 pub fn serialize(&self) -> Result<Vec<u8>, Error<C>> {
457 serialization::Serialize::serialize(&self)
458 }
459
460 pub fn deserialize(bytes: &[u8]) -> Result<Self, Error<C>> {
462 serialization::Deserialize::deserialize(bytes)
463 }
464}
465
466#[derive(Clone, PartialEq, Eq)]
469pub struct GroupCommitment<C: Ciphersuite>(pub(crate) Element<C>);
470
471impl<C> GroupCommitment<C>
472where
473 C: Ciphersuite,
474{
475 #[cfg_attr(feature = "internals", visibility::make(pub))]
477 #[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
478 pub(crate) fn to_element(self) -> <C::Group as Group>::Element {
479 self.0
480 }
481
482 #[cfg(feature = "internals")]
484 pub fn from_element(element: Element<C>) -> Self {
485 Self(element)
486 }
487}
488
489#[cfg_attr(feature = "internals", visibility::make(pub))]
496#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
497fn compute_group_commitment<C>(
498 signing_package: &SigningPackage<C>,
499 binding_factor_list: &BindingFactorList<C>,
500) -> Result<GroupCommitment<C>, Error<C>>
501where
502 C: Ciphersuite,
503{
504 let identity = <C::Group as Group>::identity();
505
506 let mut group_commitment = <C::Group as Group>::identity();
507
508 let n = signing_package.signing_commitments().len();
510
511 let mut binding_scalars = Vec::with_capacity(n);
512
513 let mut binding_elements = Vec::with_capacity(n);
514
515 for (commitment_identifier, commitment) in signing_package.signing_commitments() {
516 if identity == commitment.binding.value() || identity == commitment.hiding.value() {
519 return Err(Error::IdentityCommitment);
520 }
521
522 let binding_factor = binding_factor_list
523 .get(commitment_identifier)
524 .ok_or(Error::UnknownIdentifier)?;
525
526 binding_elements.push(commitment.binding.value());
529 binding_scalars.push(binding_factor.0);
530
531 group_commitment = group_commitment + commitment.hiding.value();
532 }
533
534 let accumulated_binding_commitment: Element<C> =
535 VartimeMultiscalarMul::<C>::vartime_multiscalar_mul(binding_scalars, binding_elements);
536
537 group_commitment = group_commitment + accumulated_binding_commitment;
538
539 Ok(GroupCommitment(group_commitment))
540}
541
542pub fn aggregate<C>(
565 signing_package: &SigningPackage<C>,
566 signature_shares: &BTreeMap<Identifier<C>, round2::SignatureShare<C>>,
567 pubkeys: &keys::PublicKeyPackage<C>,
568) -> Result<Signature<C>, Error<C>>
569where
570 C: Ciphersuite,
571{
572 if signing_package.signing_commitments().len() != signature_shares.len() {
575 return Err(Error::UnknownIdentifier);
576 }
577
578 if !signing_package.signing_commitments().keys().all(|id| {
579 #[cfg(feature = "cheater-detection")]
580 return signature_shares.contains_key(id) && pubkeys.verifying_shares().contains_key(id);
581 #[cfg(not(feature = "cheater-detection"))]
582 return signature_shares.contains_key(id);
583 }) {
584 return Err(Error::UnknownIdentifier);
585 }
586
587 let (signing_package, signature_shares, pubkeys) =
588 <C>::pre_aggregate(signing_package, signature_shares, pubkeys)?;
589
590 let binding_factor_list: BindingFactorList<C> =
593 compute_binding_factor_list(&signing_package, &pubkeys.verifying_key, &[])?;
594 let group_commitment = compute_group_commitment(&signing_package, &binding_factor_list)?;
596
597 let mut z = <<C::Group as Group>::Field>::zero();
604
605 for signature_share in signature_shares.values() {
606 z = z + signature_share.to_scalar();
607 }
608
609 let signature = Signature {
610 R: group_commitment.0,
611 z,
612 };
613
614 let verification_result = pubkeys
616 .verifying_key
617 .verify(signing_package.message(), &signature);
618
619 #[cfg(feature = "cheater-detection")]
623 if verification_result.is_err() {
624 detect_cheater(
625 &group_commitment,
626 &pubkeys,
627 &signing_package,
628 &signature_shares,
629 &binding_factor_list,
630 )?;
631 }
632
633 #[cfg(not(feature = "cheater-detection"))]
634 verification_result?;
635
636 Ok(signature)
637}
638
639#[cfg(feature = "cheater-detection")]
642fn detect_cheater<C: Ciphersuite>(
643 group_commitment: &GroupCommitment<C>,
644 pubkeys: &keys::PublicKeyPackage<C>,
645 signing_package: &SigningPackage<C>,
646 signature_shares: &BTreeMap<Identifier<C>, round2::SignatureShare<C>>,
647 binding_factor_list: &BindingFactorList<C>,
648) -> Result<(), Error<C>> {
649 let challenge = <C>::challenge(
651 &group_commitment.0,
652 &pubkeys.verifying_key,
653 signing_package.message(),
654 )?;
655
656 for (identifier, signature_share) in signature_shares {
658 let verifying_share = pubkeys
661 .verifying_shares
662 .get(identifier)
663 .ok_or(Error::UnknownIdentifier)?;
664
665 verify_signature_share_precomputed(
666 *identifier,
667 signing_package,
668 binding_factor_list,
669 group_commitment,
670 signature_share,
671 verifying_share,
672 challenge,
673 )?;
674 }
675
676 Err(Error::InvalidSignature)
678}
679
680pub fn verify_signature_share<C: Ciphersuite>(
689 identifier: Identifier<C>,
690 verifying_share: &keys::VerifyingShare<C>,
691 signature_share: &round2::SignatureShare<C>,
692 signing_package: &SigningPackage<C>,
693 verifying_key: &VerifyingKey<C>,
694) -> Result<(), Error<C>> {
695 let signature_shares = BTreeMap::from([(identifier, *signature_share)]);
697 let verifying_shares = BTreeMap::from([(identifier, *verifying_share)]);
698 let public_key_package = PublicKeyPackage::new(verifying_shares, *verifying_key);
699
700 let (signing_package, signature_shares, pubkeys) =
701 <C>::pre_aggregate(signing_package, &signature_shares, &public_key_package)?;
702
703 let verifying_share = pubkeys
705 .verifying_shares()
706 .get(&identifier)
707 .expect("pre_aggregate() must keep the identifiers");
708 let verifying_key = pubkeys.verifying_key();
709 let signature_share = signature_shares
710 .get(&identifier)
711 .expect("pre_aggregate() must keep the identifiers");
712
713 let binding_factor_list: BindingFactorList<C> =
716 compute_binding_factor_list(&signing_package, verifying_key, &[])?;
717
718 let group_commitment = compute_group_commitment(&signing_package, &binding_factor_list)?;
720
721 let challenge = <C>::challenge(
723 &group_commitment.clone().to_element(),
724 verifying_key,
725 signing_package.message().as_slice(),
726 )?;
727
728 verify_signature_share_precomputed(
729 identifier,
730 &signing_package,
731 &binding_factor_list,
732 &group_commitment,
733 signature_share,
734 verifying_share,
735 challenge,
736 )
737}
738
739#[cfg_attr(feature = "internals", visibility::make(pub))]
742#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
743fn verify_signature_share_precomputed<C: Ciphersuite>(
744 signature_share_identifier: Identifier<C>,
745 signing_package: &SigningPackage<C>,
746 binding_factor_list: &BindingFactorList<C>,
747 group_commitment: &GroupCommitment<C>,
748 signature_share: &round2::SignatureShare<C>,
749 verifying_share: &keys::VerifyingShare<C>,
750 challenge: Challenge<C>,
751) -> Result<(), Error<C>> {
752 let lambda_i = derive_interpolating_value(&signature_share_identifier, signing_package)?;
753
754 let binding_factor = binding_factor_list
755 .get(&signature_share_identifier)
756 .ok_or(Error::UnknownIdentifier)?;
757
758 let R_share = signing_package
759 .signing_commitment(&signature_share_identifier)
760 .ok_or(Error::UnknownIdentifier)?
761 .to_group_commitment_share(binding_factor);
762
763 <C>::verify_share(
765 group_commitment,
766 signature_share,
767 signature_share_identifier,
768 &R_share,
769 verifying_share,
770 lambda_i,
771 &challenge,
772 )?;
773
774 Ok(())
775}