frost_rerandomized_myecoria/
lib.rs

1//! FROST implementation supporting re-randomizable keys.
2//!
3//! To sign with re-randomized FROST:
4//!
5//! - Do Round 1 the same way as regular FROST;
6//! - The Coordinator should call [`RandomizedParams::new()`] and send
7//!   the [`RandomizedParams::randomizer`] to all participants, using a
8//!   confidential channel, along with the regular [`frost::SigningPackage`];
9//! - Each participant should call [`sign`] and send the resulting
10//!   [`frost::round2::SignatureShare`] back to the Coordinator;
11//! - The Coordinator should then call [`aggregate`].
12#![no_std]
13#![allow(non_snake_case)]
14
15extern crate alloc;
16
17#[cfg(any(test, feature = "test-impl"))]
18pub mod tests;
19
20use alloc::{collections::BTreeMap, string::ToString, vec::Vec};
21
22use derive_getters::Getters;
23pub use frost_core;
24
25#[cfg(feature = "serialization")]
26use frost_core::SigningPackage;
27use frost_core::{
28    self as frost,
29    keys::{KeyPackage, PublicKeyPackage, SigningShare, VerifyingShare},
30    serialization::SerializableScalar,
31    Ciphersuite, Error, Field, Group, Scalar, VerifyingKey,
32};
33
34#[cfg(feature = "serde")]
35use frost_core::serde;
36
37// When pulled into `reddsa`, that has its own sibling `rand_core` import.
38// For the time being, we do not re-export this `rand_core`.
39#[cfg(feature = "serialization")]
40use rand_core::{CryptoRng, RngCore};
41
42/// Randomize the given key type for usage in a FROST signing with re-randomized keys,
43/// using the given [`RandomizedParams`].
44trait Randomize<C> {
45    fn randomize(&self, params: &RandomizedParams<C>) -> Result<Self, Error<C>>
46    where
47        Self: Sized,
48        C: Ciphersuite;
49}
50
51/// A Ciphersuite that supports rerandomization.
52pub trait RandomizedCiphersuite: Ciphersuite {
53    /// A hash function that hashes into a randomizer scalar.
54    fn hash_randomizer(m: &[u8]) -> Option<<<Self::Group as Group>::Field as Field>::Scalar>;
55}
56
57impl<C: Ciphersuite> Randomize<C> for KeyPackage<C> {
58    /// Randomize the given [`KeyPackage`] for usage in a re-randomized FROST signing,
59    /// using the given [`RandomizedParams`].
60    ///
61    /// It's recommended to use [`sign`] directly which already handles
62    /// the key package randomization.
63    ///
64    /// You MUST NOT reuse the randomized key package for more than one signing.
65    fn randomize(&self, randomized_params: &RandomizedParams<C>) -> Result<Self, Error<C>>
66    where
67        Self: Sized,
68        C: Ciphersuite,
69    {
70        let verifying_share = self.verifying_share();
71        let randomized_verifying_share = VerifyingShare::<C>::new(
72            verifying_share.to_element() + randomized_params.randomizer_element,
73        );
74
75        let signing_share = self.signing_share();
76        let randomized_signing_share =
77            SigningShare::new(signing_share.to_scalar() + randomized_params.randomizer.to_scalar());
78
79        let randomized_key_package = KeyPackage::new(
80            *self.identifier(),
81            randomized_signing_share,
82            randomized_verifying_share,
83            randomized_params.randomized_verifying_key,
84            *self.min_signers(),
85        );
86        Ok(randomized_key_package)
87    }
88}
89
90impl<C: Ciphersuite> Randomize<C> for PublicKeyPackage<C> {
91    /// Randomized the given [`PublicKeyPackage`] for usage in a re-randomized FROST
92    /// aggregation, using the given [`RandomizedParams`].
93    ///
94    /// It's recommended to use [`aggregate`] directly which already handles
95    /// the public key package randomization.
96    fn randomize(&self, randomized_params: &RandomizedParams<C>) -> Result<Self, Error<C>>
97    where
98        Self: Sized,
99        C: Ciphersuite,
100    {
101        let verifying_shares = self.verifying_shares().clone();
102        let randomized_verifying_shares = verifying_shares
103            .iter()
104            .map(|(identifier, verifying_share)| {
105                (
106                    *identifier,
107                    VerifyingShare::<C>::new(
108                        verifying_share.to_element() + randomized_params.randomizer_element,
109                    ),
110                )
111            })
112            .collect();
113
114        Ok(PublicKeyPackage::new_internal(
115            randomized_verifying_shares,
116            randomized_params.randomized_verifying_key,
117            self.min_signers(),
118        ))
119    }
120}
121
122/// Re-randomized FROST signing using the given `randomizer`, which should
123/// be sent from the Coordinator using a confidential channel.
124///
125/// See [`frost::round2::sign`] for documentation on the other parameters.
126pub fn sign<C: RandomizedCiphersuite>(
127    signing_package: &frost::SigningPackage<C>,
128    signer_nonces: &frost::round1::SigningNonces<C>,
129    key_package: &frost::keys::KeyPackage<C>,
130    randomizer: Randomizer<C>,
131) -> Result<frost::round2::SignatureShare<C>, Error<C>> {
132    let randomized_params =
133        RandomizedParams::from_randomizer(key_package.verifying_key(), randomizer);
134    let randomized_key_package = key_package.randomize(&randomized_params)?;
135    frost::round2::sign(signing_package, signer_nonces, &randomized_key_package)
136}
137
138/// Re-randomized FROST signature share aggregation with the given [`RandomizedParams`],
139/// which can be computed from the previously generated randomizer using
140/// [`RandomizedParams::from_randomizer`].
141///
142/// See [`frost::aggregate`] for documentation on the other parameters.
143pub fn aggregate<C>(
144    signing_package: &frost::SigningPackage<C>,
145    signature_shares: &BTreeMap<frost::Identifier<C>, frost::round2::SignatureShare<C>>,
146    pubkeys: &frost::keys::PublicKeyPackage<C>,
147    randomized_params: &RandomizedParams<C>,
148) -> Result<frost_core::Signature<C>, Error<C>>
149where
150    C: Ciphersuite,
151{
152    let randomized_public_key_package = pubkeys.randomize(randomized_params)?;
153    frost::aggregate(
154        signing_package,
155        signature_shares,
156        &randomized_public_key_package,
157    )
158}
159
160/// A randomizer. A random scalar which is used to randomize the key.
161#[derive(Copy, Clone, PartialEq, Eq)]
162#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
163#[cfg_attr(feature = "serde", serde(bound = "C: Ciphersuite"))]
164#[cfg_attr(feature = "serde", serde(transparent))]
165#[cfg_attr(feature = "serde", serde(crate = "self::serde"))]
166pub struct Randomizer<C: Ciphersuite>(SerializableScalar<C>);
167
168impl<C> Randomizer<C>
169where
170    C: Ciphersuite,
171{
172    pub(crate) fn to_scalar(self) -> Scalar<C> {
173        self.0 .0
174    }
175}
176
177impl<C> Randomizer<C>
178where
179    C: RandomizedCiphersuite,
180{
181    /// Create a new random Randomizer.
182    ///
183    /// The [`SigningPackage`] must be the signing package being used in the
184    /// current FROST signing run. It is hashed into the randomizer calculation,
185    /// which binds it to that specific package.
186    #[cfg(feature = "serialization")]
187    pub fn new<R: RngCore + CryptoRng>(
188        mut rng: R,
189        signing_package: &SigningPackage<C>,
190    ) -> Result<Self, Error<C>> {
191        let rng_randomizer = <<C::Group as Group>::Field as Field>::random(&mut rng);
192        Self::from_randomizer_and_signing_package(rng_randomizer, signing_package)
193    }
194
195    /// Create a final Randomizer from a random Randomizer and a SigningPackage.
196    /// Function refactored out for testing, should always be private.
197    #[cfg(feature = "serialization")]
198    fn from_randomizer_and_signing_package(
199        rng_randomizer: <<<C as Ciphersuite>::Group as Group>::Field as Field>::Scalar,
200        signing_package: &SigningPackage<C>,
201    ) -> Result<Randomizer<C>, Error<C>>
202    where
203        C: RandomizedCiphersuite,
204    {
205        let randomizer = C::hash_randomizer(
206            &[
207                <<C::Group as Group>::Field>::serialize(&rng_randomizer).as_ref(),
208                &signing_package.serialize()?,
209            ]
210            .concat(),
211        )
212        .ok_or(Error::SerializationError)?;
213        Ok(Self(SerializableScalar(randomizer)))
214    }
215}
216
217impl<C> Randomizer<C>
218where
219    C: Ciphersuite,
220{
221    /// Create a new Randomizer from the given scalar. It MUST be randomly
222    /// generated.
223    ///
224    /// It is not recommended to use this method unless for compatibility
225    /// reasons with specifications on how the randomizer must be generated. Use
226    /// [`Randomizer::new()`] instead.
227    pub fn from_scalar(scalar: Scalar<C>) -> Self {
228        Self(SerializableScalar(scalar))
229    }
230
231    /// Serialize the identifier using the ciphersuite encoding.
232    pub fn serialize(&self) -> Vec<u8> {
233        self.0.serialize()
234    }
235
236    /// Deserialize an Identifier from a serialized buffer.
237    /// Returns an error if it attempts to deserialize zero.
238    pub fn deserialize(buf: &[u8]) -> Result<Self, Error<C>> {
239        Ok(Self(SerializableScalar::deserialize(buf)?))
240    }
241}
242
243impl<C> core::fmt::Debug for Randomizer<C>
244where
245    C: Ciphersuite,
246{
247    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
248        f.debug_tuple("Randomizer")
249            .field(&hex::encode(self.0.serialize()))
250            .finish()
251    }
252}
253
254/// Randomized parameters for a signing instance of randomized FROST.
255#[derive(Clone, PartialEq, Eq, Getters)]
256pub struct RandomizedParams<C: Ciphersuite> {
257    /// The randomizer, also called α
258    randomizer: Randomizer<C>,
259    /// The generator multiplied by the randomizer.
260    randomizer_element: <C::Group as Group>::Element,
261    /// The randomized group public key. The group public key added to the randomizer element.
262    randomized_verifying_key: frost_core::VerifyingKey<C>,
263}
264
265impl<C> RandomizedParams<C>
266where
267    C: RandomizedCiphersuite,
268{
269    /// Create a new [`RandomizedParams`] for the given [`VerifyingKey`] and
270    /// the given `participants`.
271    #[cfg(feature = "serialization")]
272    pub fn new<R: RngCore + CryptoRng>(
273        group_verifying_key: &VerifyingKey<C>,
274        signing_package: &SigningPackage<C>,
275        rng: R,
276    ) -> Result<Self, Error<C>> {
277        Ok(Self::from_randomizer(
278            group_verifying_key,
279            Randomizer::new(rng, signing_package)?,
280        ))
281    }
282}
283
284impl<C> RandomizedParams<C>
285where
286    C: Ciphersuite,
287{
288    /// Create a new [`RandomizedParams`] for the given [`VerifyingKey`] and the
289    /// given `participants` for the  given `randomizer`. The `randomizer` MUST
290    /// be generated uniformly at random! Use [`RandomizedParams::new()`] which
291    /// generates a fresh randomizer, unless your application requires generating
292    /// a randomizer outside.
293    pub fn from_randomizer(
294        group_verifying_key: &VerifyingKey<C>,
295        randomizer: Randomizer<C>,
296    ) -> Self {
297        let randomizer_element = <C::Group as Group>::generator() * randomizer.to_scalar();
298        let verifying_key_element = group_verifying_key.to_element();
299        let randomized_verifying_key_element = verifying_key_element + randomizer_element;
300        let randomized_verifying_key = VerifyingKey::<C>::new(randomized_verifying_key_element);
301
302        Self {
303            randomizer,
304            randomizer_element,
305            randomized_verifying_key,
306        }
307    }
308}
309
310impl<C> core::fmt::Debug for RandomizedParams<C>
311where
312    C: Ciphersuite,
313{
314    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
315        f.debug_struct("RandomizedParams")
316            .field("randomizer", &self.randomizer)
317            .field(
318                "randomizer_element",
319                &<C::Group as Group>::serialize(&self.randomizer_element)
320                    .map(hex::encode)
321                    .unwrap_or("<invalid>".to_string()),
322            )
323            .field("randomized_verifying_key", &self.randomized_verifying_key)
324            .finish()
325    }
326}