1#![no_std]
2#![allow(non_snake_case)]
3#![deny(missing_docs)]
4#![cfg_attr(docsrs, feature(doc_auto_cfg))]
5#![cfg_attr(docsrs, feature(doc_cfg))]
6#![doc = include_str!("../README.md")]
7#![doc = document_features::document_features!()]
8
9extern crate alloc;
10
11use alloc::vec;
12use alloc::{borrow::Cow, collections::BTreeMap, vec::Vec};
13
14use frost_rerandomized::RandomizedCiphersuite;
15use k256::elliptic_curve::ops::Reduce;
16use k256::{
17 elliptic_curve::{
18 bigint::U256,
19 group::prime::PrimeCurveAffine,
20 hash2curve::{hash_to_field, ExpandMsgXmd},
21 point::AffineCoordinates,
22 sec1::{FromEncodedPoint, ToEncodedPoint},
23 Field as FFField, PrimeField,
24 },
25 AffinePoint, ProjectivePoint, Scalar,
26};
27use rand_core::{CryptoRng, RngCore};
28use sha2::{Digest, Sha256};
29
30use frost_core::{self as frost, random_nonzero};
31
32use keys::EvenY;
33use keys::Tweak;
34
35#[cfg(test)]
36mod tests;
37
38#[cfg(feature = "serde")]
40pub use frost_core::serde;
41pub use frost_core::{
42 Challenge, Ciphersuite, Element, Field, FieldError, Group, GroupCommitment, GroupError,
43};
44pub use rand_core;
45
46pub type Error = frost_core::Error<Secp256K1Sha256TR>;
48
49#[derive(Clone, Copy)]
51pub struct Secp256K1ScalarField;
52
53impl Field for Secp256K1ScalarField {
54 type Scalar = Scalar;
55
56 type Serialization = [u8; 32];
57
58 fn zero() -> Self::Scalar {
59 Scalar::ZERO
60 }
61
62 fn one() -> Self::Scalar {
63 Scalar::ONE
64 }
65
66 fn invert(scalar: &Self::Scalar) -> Result<Self::Scalar, FieldError> {
67 if *scalar == <Self as Field>::zero() {
69 Err(FieldError::InvalidZeroScalar)
70 } else {
71 Ok(scalar.invert().unwrap())
72 }
73 }
74
75 fn random<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar {
76 Scalar::random(rng)
77 }
78
79 fn serialize(scalar: &Self::Scalar) -> Self::Serialization {
80 scalar.to_bytes().into()
81 }
82
83 fn deserialize(buf: &Self::Serialization) -> Result<Self::Scalar, FieldError> {
84 let field_bytes: &k256::FieldBytes = buf.into();
85 match Scalar::from_repr(*field_bytes).into() {
86 Some(s) => Ok(s),
87 None => Err(FieldError::MalformedScalar),
88 }
89 }
90
91 fn little_endian_serialize(scalar: &Self::Scalar) -> Self::Serialization {
92 let mut array = Self::serialize(scalar);
93 array.reverse();
94 array
95 }
96}
97
98#[derive(Clone, Copy, PartialEq, Eq)]
100pub struct Secp256K1Group;
101
102impl Group for Secp256K1Group {
103 type Field = Secp256K1ScalarField;
104
105 type Element = ProjectivePoint;
106
107 type Serialization = [u8; 33];
116
117 fn cofactor() -> <Self::Field as Field>::Scalar {
118 Scalar::ONE
119 }
120
121 fn identity() -> Self::Element {
122 ProjectivePoint::IDENTITY
123 }
124
125 fn generator() -> Self::Element {
126 ProjectivePoint::GENERATOR
127 }
128
129 fn serialize(element: &Self::Element) -> Result<Self::Serialization, GroupError> {
130 if *element == Self::identity() {
131 return Err(GroupError::InvalidIdentityElement);
132 }
133 let mut fixed_serialized = [0; 33];
134 let serialized_point = element.to_affine().to_encoded_point(true);
135 let serialized = serialized_point.as_bytes();
136 fixed_serialized.copy_from_slice(serialized);
137 Ok(fixed_serialized)
138 }
139
140 fn deserialize(buf: &Self::Serialization) -> Result<Self::Element, GroupError> {
141 let encoded_point =
142 k256::EncodedPoint::from_bytes(buf).map_err(|_| GroupError::MalformedElement)?;
143
144 match Option::<AffinePoint>::from(AffinePoint::from_encoded_point(&encoded_point)) {
145 Some(point) => {
146 if point.is_identity().into() {
147 Err(GroupError::InvalidIdentityElement)
151 } else {
152 Ok(ProjectivePoint::from(point))
153 }
154 }
155 None => Err(GroupError::MalformedElement),
156 }
157 }
158}
159
160fn hash_to_array(inputs: &[&[u8]]) -> [u8; 32] {
161 let mut h = Sha256::new();
162 for i in inputs {
163 h.update(i);
164 }
165 let mut output = [0u8; 32];
166 output.copy_from_slice(h.finalize().as_slice());
167 output
168}
169
170fn hash_to_scalar(domain: &[&[u8]], msg: &[u8]) -> Scalar {
171 let mut u = [Secp256K1ScalarField::zero()];
172 hash_to_field::<ExpandMsgXmd<Sha256>, Scalar>(&[msg], domain, &mut u)
173 .expect("should never return error according to error cases described in ExpandMsgXmd");
174 u[0]
175}
176
177const CONTEXT_STRING: &str = "FROST-secp256k1-SHA256-TR-v1";
181
182#[derive(Clone, Copy, PartialEq, Eq, Debug)]
184pub struct Secp256K1Sha256TR;
185
186fn hasher_to_scalar(hasher: Sha256) -> Scalar {
188 Scalar::reduce(U256::from_be_slice(&hasher.finalize()))
192}
193
194fn tagged_hash(tag: &str) -> Sha256 {
196 let mut hasher = Sha256::new();
197 let mut tag_hasher = Sha256::new();
198 tag_hasher.update(tag.as_bytes());
199 let tag_hash = tag_hasher.finalize();
200 hasher.update(tag_hash);
201 hasher.update(tag_hash);
202 hasher
203}
204
205fn tweak<T: AsRef<[u8]>>(
207 public_key: &<<Secp256K1Sha256TR as Ciphersuite>::Group as Group>::Element,
208 merkle_root: Option<T>,
209) -> Scalar {
210 match merkle_root {
211 None => {
212 let mut hasher = tagged_hash("TapTweak");
213 hasher.update(public_key.to_affine().x());
214 hasher_to_scalar(hasher)
215 }
216 Some(root) => {
217 let mut hasher = tagged_hash("TapTweak");
218 hasher.update(public_key.to_affine().x());
219 hasher.update(root.as_ref());
220 hasher_to_scalar(hasher)
221 }
222 }
223}
224
225fn negate_nonce(nonce: &frost_core::round1::Nonce<S>) -> frost_core::round1::Nonce<S> {
227 frost_core::round1::Nonce::<S>::from_scalar(-nonce.to_scalar())
228}
229
230fn negate_nonces(signing_nonces: &round1::SigningNonces) -> round1::SigningNonces {
232 round1::SigningNonces::from_nonces(
236 negate_nonce(signing_nonces.hiding()),
237 negate_nonce(signing_nonces.binding()),
238 )
239}
240
241impl Ciphersuite for Secp256K1Sha256TR {
242 const ID: &'static str = CONTEXT_STRING;
243
244 type Group = Secp256K1Group;
245
246 type HashOutput = [u8; 32];
247
248 type SignatureSerialization = [u8; 64];
249
250 fn H1(m: &[u8]) -> <<Self::Group as Group>::Field as Field>::Scalar {
254 hash_to_scalar(&[CONTEXT_STRING.as_bytes(), b"rho"], m)
255 }
256
257 fn H2(m: &[u8]) -> <<Self::Group as Group>::Field as Field>::Scalar {
261 let mut hasher = tagged_hash("BIP0340/challenge");
262 hasher.update(m);
263 hasher_to_scalar(hasher)
264 }
265
266 fn H3(m: &[u8]) -> <<Self::Group as Group>::Field as Field>::Scalar {
270 hash_to_scalar(&[CONTEXT_STRING.as_bytes(), b"nonce"], m)
271 }
272
273 fn H4(m: &[u8]) -> Self::HashOutput {
277 hash_to_array(&[CONTEXT_STRING.as_bytes(), b"msg", m])
278 }
279
280 fn H5(m: &[u8]) -> Self::HashOutput {
284 hash_to_array(&[CONTEXT_STRING.as_bytes(), b"com", m])
285 }
286
287 fn HDKG(m: &[u8]) -> Option<<<Self::Group as Group>::Field as Field>::Scalar> {
289 Some(hash_to_scalar(&[CONTEXT_STRING.as_bytes(), b"dkg"], m))
290 }
291
292 fn HID(m: &[u8]) -> Option<<<Self::Group as Group>::Field as Field>::Scalar> {
294 Some(hash_to_scalar(&[CONTEXT_STRING.as_bytes(), b"id"], m))
295 }
296
297 fn single_sign<R: RngCore + CryptoRng>(
299 signing_key: &SigningKey,
300 rng: R,
301 message: &[u8],
302 ) -> Signature {
303 let signing_key = signing_key.into_even_y(None);
304 signing_key.default_sign(rng, message)
305 }
306
307 fn pre_sign<'a>(
310 signing_package: &'a SigningPackage,
311 signer_nonces: &'a round1::SigningNonces,
312 key_package: &'a keys::KeyPackage,
313 ) -> Result<
314 (
315 Cow<'a, SigningPackage>,
316 Cow<'a, round1::SigningNonces>,
317 Cow<'a, keys::KeyPackage>,
318 ),
319 Error,
320 > {
321 Ok((
322 Cow::Borrowed(signing_package),
323 Cow::Borrowed(signer_nonces),
324 Cow::Owned(key_package.clone().into_even_y(None)),
325 ))
326 }
327
328 fn pre_aggregate<'a>(
331 signing_package: &'a SigningPackage,
332 signature_shares: &'a BTreeMap<Identifier, round2::SignatureShare>,
333 public_key_package: &'a keys::PublicKeyPackage,
334 ) -> Result<
335 (
336 Cow<'a, SigningPackage>,
337 Cow<'a, BTreeMap<Identifier, round2::SignatureShare>>,
338 Cow<'a, keys::PublicKeyPackage>,
339 ),
340 Error,
341 > {
342 Ok((
343 Cow::Borrowed(signing_package),
344 Cow::Borrowed(signature_shares),
345 Cow::Owned(public_key_package.clone().into_even_y(None)),
346 ))
347 }
348
349 fn pre_verify<'a>(
352 message: &'a [u8],
353 signature: &'a Signature,
354 public_key: &'a VerifyingKey,
355 ) -> Result<(Cow<'a, [u8]>, Cow<'a, Signature>, Cow<'a, VerifyingKey>), Error> {
356 let public_key = public_key.into_even_y(None);
357 let signature = signature.into_even_y(None);
358 Ok((
359 Cow::Borrowed(message),
360 Cow::Owned(signature),
361 Cow::Owned(public_key),
362 ))
363 }
364
365 fn generate_nonce<R: RngCore + CryptoRng>(
367 rng: &mut R,
368 ) -> (
369 <<Self::Group as Group>::Field as Field>::Scalar,
370 <Self::Group as Group>::Element,
371 ) {
372 let k = random_nonzero::<Self, R>(rng);
373 let R = <Self::Group>::generator() * k;
374 if R.to_affine().y_is_odd().into() {
375 (-k, -R)
376 } else {
377 (k, R)
378 }
379 }
380
381 fn challenge(
384 R: &Element<S>,
385 verifying_key: &VerifyingKey,
386 message: &[u8],
387 ) -> Result<Challenge<S>, Error> {
388 let mut preimage = vec![];
389 preimage.extend_from_slice(&R.to_affine().x());
390 preimage.extend_from_slice(&verifying_key.to_element().to_affine().x());
391 preimage.extend_from_slice(message);
392 Ok(Challenge::from_scalar(S::H2(&preimage[..])))
393 }
394
395 fn compute_signature_share(
397 group_commitment: &GroupCommitment<S>,
398 signer_nonces: &round1::SigningNonces,
399 binding_factor: frost::BindingFactor<S>,
400 lambda_i: <<Self::Group as Group>::Field as Field>::Scalar,
401 key_package: &frost::keys::KeyPackage<S>,
402 challenge: Challenge<S>,
403 ) -> round2::SignatureShare {
404 let signer_nonces = if !group_commitment.has_even_y() {
405 negate_nonces(signer_nonces)
406 } else {
407 signer_nonces.clone()
408 };
409
410 frost::round2::compute_signature_share(
411 &signer_nonces,
412 binding_factor,
413 lambda_i,
414 key_package,
415 challenge,
416 )
417 }
418
419 fn verify_share(
422 group_commitment: &GroupCommitment<S>,
423 signature_share: &frost_core::round2::SignatureShare<S>,
424 identifier: Identifier,
425 group_commitment_share: &frost_core::round1::GroupCommitmentShare<S>,
426 verifying_share: &frost_core::keys::VerifyingShare<S>,
427 lambda_i: Scalar,
428 challenge: &Challenge<S>,
429 ) -> Result<(), Error> {
430 let group_commitment_share = if !group_commitment.has_even_y() {
431 frost_core::round1::GroupCommitmentShare::from_element(
432 -group_commitment_share.to_element(),
433 )
434 } else {
435 *group_commitment_share
436 };
437 signature_share.verify(
438 identifier,
439 &group_commitment_share,
440 verifying_share,
441 lambda_i,
442 challenge,
443 )
444 }
445
446 fn serialize_signature(signature: &Signature) -> Result<Vec<u8>, Error> {
448 let R_bytes = Self::Group::serialize(signature.R())?;
449 let z_bytes = <Self::Group as Group>::Field::serialize(signature.z());
450
451 let mut bytes = vec![0u8; 64];
452 bytes[..32].copy_from_slice(&R_bytes[1..]);
453 bytes[32..].copy_from_slice(&z_bytes);
454 Ok(bytes)
455 }
456
457 fn deserialize_signature(bytes: &[u8]) -> Result<Signature, Error> {
459 if bytes.len() != 64 {
460 return Err(Error::MalformedSignature);
461 }
462
463 let mut R_bytes = [0u8; 33];
464 R_bytes[0] = 0x02; R_bytes[1..].copy_from_slice(&bytes[..32]);
466
467 let mut z_bytes = [0u8; 32];
468 z_bytes.copy_from_slice(&bytes[32..]);
469
470 let R = Self::Group::deserialize(&R_bytes)?;
471 let z = <Self::Group as Group>::Field::deserialize(&z_bytes)?;
472
473 Ok(Signature::new(R, z))
474 }
475
476 fn post_dkg(
480 key_package: keys::KeyPackage,
481 public_key_package: keys::PublicKeyPackage,
482 ) -> Result<(keys::KeyPackage, keys::PublicKeyPackage), Error> {
483 Ok((
489 key_package.tweak::<&[u8]>(None),
490 public_key_package.tweak::<&[u8]>(None),
491 ))
492 }
493}
494
495impl RandomizedCiphersuite for Secp256K1Sha256TR {
496 fn hash_randomizer(m: &[u8]) -> Option<<<Self::Group as Group>::Field as Field>::Scalar> {
497 Some(hash_to_scalar(
498 &[CONTEXT_STRING.as_bytes(), b"randomizer"],
499 m,
500 ))
501 }
502}
503
504type S = Secp256K1Sha256TR;
505
506pub type Identifier = frost::Identifier<S>;
508
509pub mod keys {
511 use super::*;
512
513 pub type IdentifierList<'a> = frost::keys::IdentifierList<'a, S>;
515
516 pub fn generate_with_dealer<RNG: RngCore + CryptoRng>(
519 max_signers: u16,
520 min_signers: u16,
521 identifiers: IdentifierList,
522 mut rng: RNG,
523 ) -> Result<(BTreeMap<Identifier, SecretShare>, PublicKeyPackage), Error> {
524 frost::keys::generate_with_dealer(max_signers, min_signers, identifiers, &mut rng)
525 }
526
527 pub fn split<R: RngCore + CryptoRng>(
534 secret: &SigningKey,
535 max_signers: u16,
536 min_signers: u16,
537 identifiers: IdentifierList,
538 rng: &mut R,
539 ) -> Result<(BTreeMap<Identifier, SecretShare>, PublicKeyPackage), Error> {
540 frost::keys::split(secret, max_signers, min_signers, identifiers, rng)
541 }
542
543 pub fn reconstruct(secret_shares: &[KeyPackage]) -> Result<SigningKey, Error> {
555 frost::keys::reconstruct(secret_shares)
556 }
557
558 pub type SecretShare = frost::keys::SecretShare<S>;
566
567 pub type SigningShare = frost::keys::SigningShare<S>;
569
570 pub type VerifyingShare = frost::keys::VerifyingShare<S>;
572
573 pub type KeyPackage = frost::keys::KeyPackage<S>;
580
581 pub type PublicKeyPackage = frost::keys::PublicKeyPackage<S>;
586
587 pub type VerifiableSecretSharingCommitment = frost::keys::VerifiableSecretSharingCommitment<S>;
600
601 pub trait EvenY {
617 fn has_even_y(&self) -> bool;
620
621 fn into_even_y(self, is_even: Option<bool>) -> Self;
625 }
626
627 impl EvenY for PublicKeyPackage {
628 fn has_even_y(&self) -> bool {
629 let verifying_key = self.verifying_key();
630 (!verifying_key.to_element().to_affine().y_is_odd()).into()
631 }
632
633 fn into_even_y(self, is_even: Option<bool>) -> Self {
634 let is_even = is_even.unwrap_or_else(|| self.has_even_y());
635 if !is_even {
636 let verifying_key = VerifyingKey::new(-self.verifying_key().to_element());
638 let verifying_shares: BTreeMap<_, _> = self
641 .verifying_shares()
642 .iter()
643 .map(|(i, vs)| {
644 let vs = VerifyingShare::new(-vs.to_element());
645 (*i, vs)
646 })
647 .collect();
648 PublicKeyPackage::new(verifying_shares, verifying_key)
649 } else {
650 self
651 }
652 }
653 }
654
655 impl EvenY for KeyPackage {
656 fn has_even_y(&self) -> bool {
657 let verifying_key = self.verifying_key();
658 (!verifying_key.to_element().to_affine().y_is_odd()).into()
659 }
660
661 fn into_even_y(self, is_even: Option<bool>) -> Self {
662 let is_even = is_even.unwrap_or_else(|| self.has_even_y());
663 if !is_even {
664 let verifying_key = VerifyingKey::new(-self.verifying_key().to_element());
666 let signing_share = SigningShare::new(-self.signing_share().to_scalar());
667 let verifying_share = VerifyingShare::new(-self.verifying_share().to_element());
668 KeyPackage::new(
669 *self.identifier(),
670 signing_share,
671 verifying_share,
672 verifying_key,
673 *self.min_signers(),
674 )
675 } else {
676 self
677 }
678 }
679 }
680
681 impl EvenY for VerifyingKey {
682 fn has_even_y(&self) -> bool {
683 (!self.to_element().to_affine().y_is_odd()).into()
684 }
685
686 fn into_even_y(self, is_even: Option<bool>) -> Self {
687 let is_even = is_even.unwrap_or_else(|| self.has_even_y());
688 if !is_even {
689 VerifyingKey::new(-self.to_element())
690 } else {
691 self
692 }
693 }
694 }
695
696 impl EvenY for GroupCommitment<S> {
697 fn has_even_y(&self) -> bool {
698 (!self.clone().to_element().to_affine().y_is_odd()).into()
699 }
700
701 fn into_even_y(self, is_even: Option<bool>) -> Self {
702 let is_even = is_even.unwrap_or_else(|| self.has_even_y());
703 if !is_even {
704 Self::from_element(-self.to_element())
705 } else {
706 self
707 }
708 }
709 }
710
711 impl EvenY for Signature {
712 fn has_even_y(&self) -> bool {
713 (!self.R().to_affine().y_is_odd()).into()
714 }
715
716 fn into_even_y(self, is_even: Option<bool>) -> Self {
717 let is_even = is_even.unwrap_or_else(|| self.has_even_y());
718 if !is_even {
719 Self::new(-*self.R(), *self.z())
720 } else {
721 self
722 }
723 }
724 }
725
726 impl EvenY for SigningKey {
727 fn has_even_y(&self) -> bool {
728 (!Into::<VerifyingKey>::into(self)
729 .to_element()
730 .to_affine()
731 .y_is_odd())
732 .into()
733 }
734
735 fn into_even_y(self, is_even: Option<bool>) -> Self {
736 let is_even = is_even.unwrap_or_else(|| self.has_even_y());
737 if !is_even {
738 SigningKey::from_scalar(-self.to_scalar())
739 .expect("the original SigningKey must be nonzero")
740 } else {
741 self
742 }
743 }
744 }
745
746 pub trait Tweak: EvenY {
748 fn tweak<T: AsRef<[u8]>>(self, merkle_root: Option<T>) -> Self;
750 }
751
752 impl Tweak for PublicKeyPackage {
753 fn tweak<T: AsRef<[u8]>>(self, merkle_root: Option<T>) -> Self {
754 let t = tweak(&self.verifying_key().to_element(), merkle_root);
755 let tp = ProjectivePoint::GENERATOR * t;
756 let public_key_package = self.into_even_y(None);
757 let verifying_key =
758 VerifyingKey::new(public_key_package.verifying_key().to_element() + tp);
759 let verifying_shares: BTreeMap<_, _> = public_key_package
762 .verifying_shares()
763 .iter()
764 .map(|(i, vs)| {
765 let vs = VerifyingShare::new(vs.to_element() + tp);
766 (*i, vs)
767 })
768 .collect();
769 PublicKeyPackage::new(verifying_shares, verifying_key)
770 }
771 }
772
773 impl Tweak for KeyPackage {
774 fn tweak<T: AsRef<[u8]>>(self, merkle_root: Option<T>) -> Self {
775 let t = tweak(&self.verifying_key().to_element(), merkle_root);
776 let tp = ProjectivePoint::GENERATOR * t;
777 let key_package = self.into_even_y(None);
778 let verifying_key = VerifyingKey::new(key_package.verifying_key().to_element() + tp);
779 let signing_share = SigningShare::new(key_package.signing_share().to_scalar() + t);
780 let verifying_share =
781 VerifyingShare::new(key_package.verifying_share().to_element() + tp);
782 KeyPackage::new(
783 *key_package.identifier(),
784 signing_share,
785 verifying_share,
786 verifying_key,
787 *key_package.min_signers(),
788 )
789 }
790 }
791
792 pub mod dkg;
793 pub mod refresh;
794 pub mod repairable;
795}
796
797pub mod round1 {
799 use crate::keys::SigningShare;
800
801 use super::*;
802
803 pub type SigningNonces = frost::round1::SigningNonces<S>;
809
810 pub type SigningCommitments = frost::round1::SigningCommitments<S>;
815
816 pub type NonceCommitment = frost::round1::NonceCommitment<S>;
818
819 pub fn commit<RNG>(secret: &SigningShare, rng: &mut RNG) -> (SigningNonces, SigningCommitments)
824 where
825 RNG: CryptoRng + RngCore,
826 {
827 frost::round1::commit::<S, RNG>(secret, rng)
828 }
829}
830
831pub type SigningPackage = frost::SigningPackage<S>;
834
835pub mod round2 {
837 use keys::Tweak;
838
839 use super::*;
840
841 pub type SignatureShare = frost::round2::SignatureShare<S>;
844
845 pub fn sign(
854 signing_package: &SigningPackage,
855 signer_nonces: &round1::SigningNonces,
856 key_package: &keys::KeyPackage,
857 ) -> Result<SignatureShare, Error> {
858 frost::round2::sign(signing_package, signer_nonces, key_package)
859 }
860
861 pub fn sign_with_tweak(
863 signing_package: &SigningPackage,
864 signer_nonces: &round1::SigningNonces,
865 key_package: &keys::KeyPackage,
866 merkle_root: Option<&[u8]>,
867 ) -> Result<SignatureShare, Error> {
868 let key_package = key_package.clone().tweak(merkle_root);
869 frost::round2::sign(signing_package, signer_nonces, &key_package)
870 }
871}
872
873pub type Signature = frost_core::Signature<S>;
875
876pub fn aggregate(
892 signing_package: &SigningPackage,
893 signature_shares: &BTreeMap<Identifier, round2::SignatureShare>,
894 public_key_package: &keys::PublicKeyPackage,
895) -> Result<Signature, Error> {
896 frost::aggregate(signing_package, signature_shares, public_key_package)
897}
898
899pub fn aggregate_with_tweak(
901 signing_package: &SigningPackage,
902 signature_shares: &BTreeMap<Identifier, round2::SignatureShare>,
903 public_key_package: &keys::PublicKeyPackage,
904 merkle_root: Option<&[u8]>,
905) -> Result<Signature, Error> {
906 let public_key_package = public_key_package.clone().tweak(merkle_root);
907 frost::aggregate(signing_package, signature_shares, &public_key_package)
908}
909
910pub type SigningKey = frost_core::SigningKey<S>;
912
913pub type VerifyingKey = frost_core::VerifyingKey<S>;