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_cfg))]
10#![doc = include_str!("../README.md")]
11#![doc = document_features::document_features!()]
12
13#[macro_use]
14extern crate alloc;
15
16use core::marker::PhantomData;
17
18use alloc::{
19 collections::{BTreeMap, BTreeSet},
20 fmt::{self, Debug},
21 vec::Vec,
22};
23
24use derive_getters::Getters;
25#[cfg(any(test, feature = "test-impl"))]
26use hex::FromHex;
27use keys::PublicKeyPackage;
28use rand_core::{CryptoRng, RngCore};
29use serialization::SerializableScalar;
30use zeroize::Zeroize;
31
32pub mod batch;
33#[cfg(any(test, feature = "test-impl"))]
34pub mod benches;
35mod error;
36mod identifier;
37pub mod keys;
38pub mod round1;
39pub mod round2;
40mod scalar_mul;
41pub mod serialization;
45mod signature;
46mod signing_key;
47#[cfg(any(test, feature = "test-impl"))]
48pub mod tests;
49mod traits;
50mod verifying_key;
51
52pub use error::{Error, FieldError, GroupError};
53pub use identifier::Identifier;
54use scalar_mul::VartimeMultiscalarMul;
55#[cfg(feature = "serde")]
57pub use serde;
58pub use signature::Signature;
59pub use signing_key::SigningKey;
60pub use traits::{Ciphersuite, Element, Field, Group, Scalar};
61pub use verifying_key::VerifyingKey;
62
63#[derive(Copy, Clone)]
67pub struct Challenge<C: Ciphersuite>(pub(crate) <<C::Group as Group>::Field as Field>::Scalar);
68
69impl<C> Challenge<C>
70where
71 C: Ciphersuite,
72{
73 #[cfg_attr(feature = "internals", visibility::make(pub))]
75 #[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
76 #[allow(dead_code)]
77 pub(crate) fn from_scalar(
78 scalar: <<<C as Ciphersuite>::Group as Group>::Field as Field>::Scalar,
79 ) -> Self {
80 Self(scalar)
81 }
82
83 #[cfg_attr(feature = "internals", visibility::make(pub))]
85 #[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
86 pub(crate) fn to_scalar(
87 self,
88 ) -> <<<C as Ciphersuite>::Group as Group>::Field as Field>::Scalar {
89 self.0
90 }
91}
92
93impl<C> Debug for Challenge<C>
94where
95 C: Ciphersuite,
96{
97 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
98 f.debug_tuple("Secret")
99 .field(&hex::encode(<<C::Group as Group>::Field>::serialize(
100 &self.0,
101 )))
102 .finish()
103 }
104}
105
106#[cfg_attr(feature = "internals", visibility::make(pub))]
116#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
117fn challenge<C>(
118 R: &Element<C>,
119 verifying_key: &VerifyingKey<C>,
120 msg: &[u8],
121) -> Result<Challenge<C>, Error<C>>
122where
123 C: Ciphersuite,
124{
125 let mut preimage = Vec::new();
126
127 preimage.extend_from_slice(<C::Group>::serialize(R)?.as_ref());
128 preimage.extend_from_slice(<C::Group>::serialize(&verifying_key.to_element())?.as_ref());
129 preimage.extend_from_slice(msg);
130
131 Ok(Challenge(C::H2(&preimage[..])))
132}
133
134#[cfg_attr(feature = "internals", visibility::make(pub))]
138#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
139pub(crate) fn random_nonzero<C: Ciphersuite, R: RngCore + CryptoRng>(rng: &mut R) -> Scalar<C> {
140 loop {
141 let scalar = <<C::Group as Group>::Field>::random(rng);
142
143 if scalar != <<C::Group as Group>::Field>::zero() {
144 return scalar;
145 }
146 }
147}
148
149#[derive(Copy, Clone, Debug, PartialEq, Eq, Zeroize)]
150#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
151#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
152struct Header<C: Ciphersuite> {
153 #[cfg_attr(
155 feature = "serde",
156 serde(deserialize_with = "crate::serialization::version_deserialize::<_>")
157 )]
158 version: u8,
159 #[cfg_attr(
161 feature = "serde",
162 serde(serialize_with = "crate::serialization::ciphersuite_serialize::<_, C>")
163 )]
164 #[cfg_attr(
165 feature = "serde",
166 serde(deserialize_with = "crate::serialization::ciphersuite_deserialize::<_, C>")
167 )]
168 ciphersuite: (),
169 #[cfg_attr(feature = "serde", serde(skip))]
170 phantom: PhantomData<C>,
171}
172
173impl<C> Default for Header<C>
174where
175 C: Ciphersuite,
176{
177 fn default() -> Self {
178 Self {
179 version: Default::default(),
180 ciphersuite: Default::default(),
181 phantom: Default::default(),
182 }
183 }
184}
185
186#[derive(Clone, PartialEq, Eq)]
193pub struct BindingFactor<C: Ciphersuite>(Scalar<C>);
194
195impl<C> BindingFactor<C>
196where
197 C: Ciphersuite,
198{
199 pub fn serialize(&self) -> Vec<u8> {
201 SerializableScalar::<C>(self.0).serialize()
202 }
203}
204
205impl<C> Debug for BindingFactor<C>
206where
207 C: Ciphersuite,
208{
209 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
210 f.debug_tuple("BindingFactor")
211 .field(&hex::encode(self.serialize()))
212 .finish()
213 }
214}
215
216#[derive(Clone)]
218pub struct BindingFactorList<C: Ciphersuite>(BTreeMap<Identifier<C>, BindingFactor<C>>);
219
220impl<C> BindingFactorList<C>
221where
222 C: Ciphersuite,
223{
224 #[cfg(feature = "internals")]
226 pub fn new(binding_factors: BTreeMap<Identifier<C>, BindingFactor<C>>) -> Self {
227 Self(binding_factors)
228 }
229
230 pub fn get(&self, key: &Identifier<C>) -> Option<&BindingFactor<C>> {
232 self.0.get(key)
233 }
234}
235
236#[cfg_attr(feature = "internals", visibility::make(pub))]
240#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
241pub(crate) fn compute_binding_factor_list<C>(
242 signing_package: &SigningPackage<C>,
243 verifying_key: &VerifyingKey<C>,
244 additional_prefix: &[u8],
245) -> Result<BindingFactorList<C>, Error<C>>
246where
247 C: Ciphersuite,
248{
249 let preimages = signing_package.binding_factor_preimages(verifying_key, additional_prefix)?;
250
251 Ok(BindingFactorList(
252 preimages
253 .iter()
254 .map(|(identifier, preimage)| {
255 let binding_factor = C::H1(preimage);
256 (*identifier, BindingFactor(binding_factor))
257 })
258 .collect(),
259 ))
260}
261
262#[cfg(any(test, feature = "test-impl"))]
263impl<C> FromHex for BindingFactor<C>
264where
265 C: Ciphersuite,
266{
267 type Error = &'static str;
268
269 fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
270 let v: Vec<u8> = FromHex::from_hex(hex).map_err(|_| "invalid hex")?;
271
272 let ret = match v.as_slice().try_into() {
273 Ok(bytes) => <<C::Group as Group>::Field>::deserialize(&bytes)
274 .map(|scalar| Self(scalar))
275 .map_err(|_| "malformed scalar encoding"),
276 Err(_) => Err("malformed scalar encoding"),
277 };
278 ret
279 }
280}
281
282#[cfg_attr(feature = "internals", visibility::make(pub))]
294#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
295fn compute_lagrange_coefficient<C: Ciphersuite>(
296 x_set: &BTreeSet<Identifier<C>>,
297 x: Option<Identifier<C>>,
298 x_i: Identifier<C>,
299) -> Result<Scalar<C>, Error<C>> {
300 if x_set.is_empty() {
301 return Err(Error::IncorrectNumberOfIdentifiers);
302 }
303 let mut num = <<C::Group as Group>::Field>::one();
304 let mut den = <<C::Group as Group>::Field>::one();
305
306 let mut x_i_found = false;
307
308 for x_j in x_set.iter() {
309 if x_i == *x_j {
310 x_i_found = true;
311 continue;
312 }
313
314 if let Some(x) = x {
315 num = num * (x.to_scalar() - x_j.to_scalar());
316 den = den * (x_i.to_scalar() - x_j.to_scalar());
317 } else {
318 num = num * x_j.to_scalar();
320 den = den * (x_j.to_scalar() - x_i.to_scalar());
321 }
322 }
323 if !x_i_found {
324 return Err(Error::UnknownIdentifier);
325 }
326
327 Ok(
328 num * <<C::Group as Group>::Field>::invert(&den)
329 .map_err(|_| Error::DuplicatedIdentifier)?,
330 )
331}
332
333#[cfg_attr(feature = "internals", visibility::make(pub))]
339#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
340fn derive_interpolating_value<C: Ciphersuite>(
341 signer_id: &Identifier<C>,
342 signing_package: &SigningPackage<C>,
343) -> Result<Scalar<C>, Error<C>> {
344 compute_lagrange_coefficient(
345 &signing_package
346 .signing_commitments()
347 .keys()
348 .cloned()
349 .collect(),
350 None,
351 *signer_id,
352 )
353}
354
355#[derive(Clone, Debug, PartialEq, Eq, Getters)]
358#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
359#[cfg_attr(feature = "serde", serde(bound = "C: Ciphersuite"))]
360#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
361pub struct SigningPackage<C: Ciphersuite> {
362 #[getter(skip)]
364 pub(crate) header: Header<C>,
365 signing_commitments: BTreeMap<Identifier<C>, round1::SigningCommitments<C>>,
368 #[cfg_attr(
373 feature = "serde",
374 serde(
375 serialize_with = "serdect::slice::serialize_hex_lower_or_bin",
376 deserialize_with = "serdect::slice::deserialize_hex_or_bin_vec"
377 )
378 )]
379 message: Vec<u8>,
380}
381
382impl<C> SigningPackage<C>
383where
384 C: Ciphersuite,
385{
386 pub fn new(
390 signing_commitments: BTreeMap<Identifier<C>, round1::SigningCommitments<C>>,
391 message: &[u8],
392 ) -> SigningPackage<C> {
393 SigningPackage {
394 header: Header::default(),
395 signing_commitments,
396 message: message.to_vec(),
397 }
398 }
399
400 pub fn signing_commitment(
402 &self,
403 identifier: &Identifier<C>,
404 ) -> Option<round1::SigningCommitments<C>> {
405 self.signing_commitments.get(identifier).copied()
406 }
407
408 #[cfg_attr(feature = "internals", visibility::make(pub))]
411 #[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
412 #[allow(clippy::type_complexity)]
413 pub fn binding_factor_preimages(
414 &self,
415 verifying_key: &VerifyingKey<C>,
416 additional_prefix: &[u8],
417 ) -> Result<Vec<(Identifier<C>, Vec<u8>)>, Error<C>> {
418 let mut binding_factor_input_prefix = Vec::new();
419
420 binding_factor_input_prefix.extend_from_slice(verifying_key.serialize()?.as_ref());
424
425 binding_factor_input_prefix.extend_from_slice(C::H4(self.message.as_slice()).as_ref());
429 binding_factor_input_prefix.extend_from_slice(
430 C::H5(&round1::encode_group_commitments(self.signing_commitments())?[..]).as_ref(),
431 );
432 binding_factor_input_prefix.extend_from_slice(additional_prefix);
433
434 Ok(self
435 .signing_commitments()
436 .keys()
437 .map(|identifier| {
438 let mut binding_factor_input = Vec::new();
439
440 binding_factor_input.extend_from_slice(&binding_factor_input_prefix);
441 binding_factor_input.extend_from_slice(identifier.serialize().as_ref());
442 (*identifier, binding_factor_input)
443 })
444 .collect())
445 }
446}
447
448#[cfg(feature = "serialization")]
449impl<C> SigningPackage<C>
450where
451 C: Ciphersuite,
452{
453 pub fn serialize(&self) -> Result<Vec<u8>, Error<C>> {
455 serialization::Serialize::serialize(&self)
456 }
457
458 pub fn deserialize(bytes: &[u8]) -> Result<Self, Error<C>> {
460 serialization::Deserialize::deserialize(bytes)
461 }
462}
463
464#[derive(Clone, PartialEq, Eq)]
467pub struct GroupCommitment<C: Ciphersuite>(pub(crate) Element<C>);
468
469impl<C> GroupCommitment<C>
470where
471 C: Ciphersuite,
472{
473 #[cfg_attr(feature = "internals", visibility::make(pub))]
475 #[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
476 pub(crate) fn to_element(self) -> <C::Group as Group>::Element {
477 self.0
478 }
479
480 #[cfg(feature = "internals")]
482 pub fn from_element(element: Element<C>) -> Self {
483 Self(element)
484 }
485}
486
487#[cfg_attr(feature = "internals", visibility::make(pub))]
494#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
495fn compute_group_commitment<C>(
496 signing_package: &SigningPackage<C>,
497 binding_factor_list: &BindingFactorList<C>,
498) -> Result<GroupCommitment<C>, Error<C>>
499where
500 C: Ciphersuite,
501{
502 let identity = <C::Group as Group>::identity();
503
504 let mut group_commitment = <C::Group as Group>::identity();
505
506 let n = signing_package.signing_commitments().len();
508
509 let mut binding_scalars = Vec::with_capacity(n);
510
511 let mut binding_elements = Vec::with_capacity(n);
512
513 for (commitment_identifier, commitment) in signing_package.signing_commitments() {
514 if identity == commitment.binding.value() || identity == commitment.hiding.value() {
517 return Err(Error::IdentityCommitment);
518 }
519
520 let binding_factor = binding_factor_list
521 .get(commitment_identifier)
522 .ok_or(Error::UnknownIdentifier)?;
523
524 binding_elements.push(commitment.binding.value());
527 binding_scalars.push(binding_factor.0);
528
529 group_commitment = group_commitment + commitment.hiding.value();
530 }
531
532 let accumulated_binding_commitment: Element<C> =
533 VartimeMultiscalarMul::<C>::vartime_multiscalar_mul(binding_scalars, binding_elements);
534
535 group_commitment = group_commitment + accumulated_binding_commitment;
536
537 Ok(GroupCommitment(group_commitment))
538}
539
540pub fn aggregate<C>(
564 signing_package: &SigningPackage<C>,
565 signature_shares: &BTreeMap<Identifier<C>, round2::SignatureShare<C>>,
566 pubkeys: &keys::PublicKeyPackage<C>,
567) -> Result<Signature<C>, Error<C>>
568where
569 C: Ciphersuite,
570{
571 aggregate_custom(
572 signing_package,
573 signature_shares,
574 pubkeys,
575 CheaterDetection::FirstCheater,
576 )
577}
578
579pub enum CheaterDetection {
581 Disabled,
584 FirstCheater,
587 AllCheaters,
590}
591
592pub fn aggregate_custom<C>(
597 signing_package: &SigningPackage<C>,
598 signature_shares: &BTreeMap<Identifier<C>, round2::SignatureShare<C>>,
599 pubkeys: &keys::PublicKeyPackage<C>,
600 cheater_detection: CheaterDetection,
601) -> Result<Signature<C>, Error<C>>
602where
603 C: Ciphersuite,
604{
605 if signing_package.signing_commitments().len() != signature_shares.len() {
608 return Err(Error::UnknownIdentifier);
609 }
610
611 if let Some(min) = pubkeys.min_signers() {
612 if signature_shares.len() < min as usize {
613 return Err(Error::IncorrectNumberOfShares);
614 }
615 }
616
617 if !signing_package
618 .signing_commitments()
619 .keys()
620 .all(|id| match cheater_detection {
621 CheaterDetection::Disabled => signature_shares.contains_key(id),
622 CheaterDetection::FirstCheater | CheaterDetection::AllCheaters => {
623 signature_shares.contains_key(id) && pubkeys.verifying_shares().contains_key(id)
624 }
625 })
626 {
627 return Err(Error::UnknownIdentifier);
628 }
629
630 let (signing_package, signature_shares, pubkeys) =
631 <C>::pre_aggregate(signing_package, signature_shares, pubkeys)?;
632
633 let binding_factor_list: BindingFactorList<C> =
636 compute_binding_factor_list(&signing_package, &pubkeys.verifying_key, &[])?;
637
638 let signing_package = <C>::pre_commitment_aggregate(&signing_package, &binding_factor_list)?;
640 let group_commitment = compute_group_commitment(&signing_package, &binding_factor_list)?;
641
642 let mut z = <<C::Group as Group>::Field>::zero();
649
650 for signature_share in signature_shares.values() {
651 z = z + signature_share.to_scalar();
652 }
653
654 let signature = Signature {
655 R: group_commitment.0,
656 z,
657 };
658
659 let verification_result = pubkeys
661 .verifying_key
662 .verify(signing_package.message(), &signature);
663
664 match cheater_detection {
668 CheaterDetection::Disabled => {
669 verification_result?;
670 }
671 CheaterDetection::FirstCheater | CheaterDetection::AllCheaters => {
672 if verification_result.is_err() {
673 detect_cheater(
674 &group_commitment,
675 &pubkeys,
676 &signing_package,
677 &signature_shares,
678 &binding_factor_list,
679 cheater_detection,
680 )?;
681 }
682 }
683 }
684
685 Ok(signature)
686}
687
688fn detect_cheater<C: Ciphersuite>(
691 group_commitment: &GroupCommitment<C>,
692 pubkeys: &keys::PublicKeyPackage<C>,
693 signing_package: &SigningPackage<C>,
694 signature_shares: &BTreeMap<Identifier<C>, round2::SignatureShare<C>>,
695 binding_factor_list: &BindingFactorList<C>,
696 cheater_detection: CheaterDetection,
697) -> Result<(), Error<C>> {
698 let challenge = <C>::challenge(
700 &group_commitment.0,
701 &pubkeys.verifying_key,
702 signing_package.message(),
703 )?;
704
705 let mut all_culprits = Vec::new();
706
707 for (identifier, signature_share) in signature_shares {
709 let verifying_share = pubkeys
712 .verifying_shares
713 .get(identifier)
714 .ok_or(Error::UnknownIdentifier)?;
715
716 let r = verify_signature_share_precomputed(
717 *identifier,
718 signing_package,
719 binding_factor_list,
720 group_commitment,
721 signature_share,
722 verifying_share,
723 challenge,
724 );
725 match r {
726 Ok(_) => {}
727 Err(Error::InvalidSignatureShare { culprits }) => {
728 all_culprits.extend(culprits);
729 if let CheaterDetection::FirstCheater = cheater_detection {
730 break;
731 }
732 }
733 Err(e) => return Err(e),
734 }
735 }
736 if !all_culprits.is_empty() {
737 return Err(Error::InvalidSignatureShare {
738 culprits: all_culprits,
739 });
740 }
741
742 Err(Error::InvalidSignature)
744}
745
746pub fn verify_signature_share<C: Ciphersuite>(
755 identifier: Identifier<C>,
756 verifying_share: &keys::VerifyingShare<C>,
757 signature_share: &round2::SignatureShare<C>,
758 signing_package: &SigningPackage<C>,
759 verifying_key: &VerifyingKey<C>,
760) -> Result<(), Error<C>> {
761 let signature_shares = BTreeMap::from([(identifier, *signature_share)]);
763 let verifying_shares = BTreeMap::from([(identifier, *verifying_share)]);
764 let public_key_package = PublicKeyPackage {
765 verifying_shares,
766 verifying_key: *verifying_key,
767 min_signers: None,
771 header: Header::default(),
772 };
773
774 let (signing_package, signature_shares, pubkeys) =
775 <C>::pre_aggregate(signing_package, &signature_shares, &public_key_package)?;
776
777 let verifying_share = pubkeys
779 .verifying_shares()
780 .get(&identifier)
781 .ok_or(Error::UnknownIdentifier)?;
782 let verifying_key = pubkeys.verifying_key();
783 let signature_share = signature_shares
784 .get(&identifier)
785 .ok_or(Error::UnknownIdentifier)?;
786
787 let binding_factor_list: BindingFactorList<C> =
790 compute_binding_factor_list(&signing_package, verifying_key, &[])?;
791
792 let signing_package = <C>::pre_commitment_aggregate(&signing_package, &binding_factor_list)?;
793
794 let group_commitment = compute_group_commitment(&signing_package, &binding_factor_list)?;
796
797 let challenge = <C>::challenge(
799 &group_commitment.clone().to_element(),
800 verifying_key,
801 signing_package.message().as_slice(),
802 )?;
803
804 verify_signature_share_precomputed(
805 identifier,
806 &signing_package,
807 &binding_factor_list,
808 &group_commitment,
809 signature_share,
810 verifying_share,
811 challenge,
812 )
813}
814
815#[cfg_attr(feature = "internals", visibility::make(pub))]
818#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
819fn verify_signature_share_precomputed<C: Ciphersuite>(
820 signature_share_identifier: Identifier<C>,
821 signing_package: &SigningPackage<C>,
822 binding_factor_list: &BindingFactorList<C>,
823 group_commitment: &GroupCommitment<C>,
824 signature_share: &round2::SignatureShare<C>,
825 verifying_share: &keys::VerifyingShare<C>,
826 challenge: Challenge<C>,
827) -> Result<(), Error<C>> {
828 let lambda_i = derive_interpolating_value(&signature_share_identifier, signing_package)?;
829
830 let binding_factor = binding_factor_list
831 .get(&signature_share_identifier)
832 .ok_or(Error::UnknownIdentifier)?;
833
834 let R_share = signing_package
835 .signing_commitment(&signature_share_identifier)
836 .ok_or(Error::UnknownIdentifier)?
837 .to_group_commitment_share(binding_factor);
838
839 <C>::verify_share(
841 group_commitment,
842 signature_share,
843 signature_share_identifier,
844 &R_share,
845 verifying_share,
846 lambda_i,
847 &challenge,
848 )?;
849
850 Ok(())
851}