frost_rerandomized/
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(
115            randomized_verifying_shares,
116            randomized_params.randomized_verifying_key,
117        ))
118    }
119}
120
121/// Re-randomized FROST signing using the given `randomizer`, which should
122/// be sent from the Coordinator using a confidential channel.
123///
124/// See [`frost::round2::sign`] for documentation on the other parameters.
125pub fn sign<C: RandomizedCiphersuite>(
126    signing_package: &frost::SigningPackage<C>,
127    signer_nonces: &frost::round1::SigningNonces<C>,
128    key_package: &frost::keys::KeyPackage<C>,
129    randomizer: Randomizer<C>,
130) -> Result<frost::round2::SignatureShare<C>, Error<C>> {
131    let randomized_params =
132        RandomizedParams::from_randomizer(key_package.verifying_key(), randomizer);
133    let randomized_key_package = key_package.randomize(&randomized_params)?;
134    frost::round2::sign(signing_package, signer_nonces, &randomized_key_package)
135}
136
137/// Re-randomized FROST signature share aggregation with the given [`RandomizedParams`],
138/// which can be computed from the previously generated randomizer using
139/// [`RandomizedParams::from_randomizer`].
140///
141/// See [`frost::aggregate`] for documentation on the other parameters.
142pub fn aggregate<C>(
143    signing_package: &frost::SigningPackage<C>,
144    signature_shares: &BTreeMap<frost::Identifier<C>, frost::round2::SignatureShare<C>>,
145    pubkeys: &frost::keys::PublicKeyPackage<C>,
146    randomized_params: &RandomizedParams<C>,
147) -> Result<frost_core::Signature<C>, Error<C>>
148where
149    C: Ciphersuite,
150{
151    let randomized_public_key_package = pubkeys.randomize(randomized_params)?;
152    frost::aggregate(
153        signing_package,
154        signature_shares,
155        &randomized_public_key_package,
156    )
157}
158
159/// A randomizer. A random scalar which is used to randomize the key.
160#[derive(Copy, Clone, PartialEq, Eq)]
161#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
162#[cfg_attr(feature = "serde", serde(bound = "C: Ciphersuite"))]
163#[cfg_attr(feature = "serde", serde(transparent))]
164#[cfg_attr(feature = "serde", serde(crate = "self::serde"))]
165pub struct Randomizer<C: Ciphersuite>(SerializableScalar<C>);
166
167impl<C> Randomizer<C>
168where
169    C: Ciphersuite,
170{
171    pub(crate) fn to_scalar(self) -> Scalar<C> {
172        self.0 .0
173    }
174}
175
176impl<C> Randomizer<C>
177where
178    C: RandomizedCiphersuite,
179{
180    /// Create a new random Randomizer.
181    ///
182    /// The [`SigningPackage`] must be the signing package being used in the
183    /// current FROST signing run. It is hashed into the randomizer calculation,
184    /// which binds it to that specific package.
185    #[cfg(feature = "serialization")]
186    pub fn new<R: RngCore + CryptoRng>(
187        mut rng: R,
188        signing_package: &SigningPackage<C>,
189    ) -> Result<Self, Error<C>> {
190        let rng_randomizer = <<C::Group as Group>::Field as Field>::random(&mut rng);
191        Self::from_randomizer_and_signing_package(rng_randomizer, signing_package)
192    }
193
194    /// Create a final Randomizer from a random Randomizer and a SigningPackage.
195    /// Function refactored out for testing, should always be private.
196    #[cfg(feature = "serialization")]
197    fn from_randomizer_and_signing_package(
198        rng_randomizer: <<<C as Ciphersuite>::Group as Group>::Field as Field>::Scalar,
199        signing_package: &SigningPackage<C>,
200    ) -> Result<Randomizer<C>, Error<C>>
201    where
202        C: RandomizedCiphersuite,
203    {
204        let randomizer = C::hash_randomizer(
205            &[
206                <<C::Group as Group>::Field>::serialize(&rng_randomizer).as_ref(),
207                &signing_package.serialize()?,
208            ]
209            .concat(),
210        )
211        .ok_or(Error::SerializationError)?;
212        Ok(Self(SerializableScalar(randomizer)))
213    }
214}
215
216impl<C> Randomizer<C>
217where
218    C: Ciphersuite,
219{
220    /// Create a new Randomizer from the given scalar. It MUST be randomly
221    /// generated.
222    ///
223    /// It is not recommended to use this method unless for compatibility
224    /// reasons with specifications on how the randomizer must be generated. Use
225    /// [`Randomizer::new()`] instead.
226    pub fn from_scalar(scalar: Scalar<C>) -> Self {
227        Self(SerializableScalar(scalar))
228    }
229
230    /// Serialize the identifier using the ciphersuite encoding.
231    pub fn serialize(&self) -> Vec<u8> {
232        self.0.serialize()
233    }
234
235    /// Deserialize an Identifier from a serialized buffer.
236    /// Returns an error if it attempts to deserialize zero.
237    pub fn deserialize(buf: &[u8]) -> Result<Self, Error<C>> {
238        Ok(Self(SerializableScalar::deserialize(buf)?))
239    }
240}
241
242impl<C> core::fmt::Debug for Randomizer<C>
243where
244    C: Ciphersuite,
245{
246    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
247        f.debug_tuple("Randomizer")
248            .field(&hex::encode(self.0.serialize()))
249            .finish()
250    }
251}
252
253/// Randomized parameters for a signing instance of randomized FROST.
254#[derive(Clone, PartialEq, Eq, Getters)]
255pub struct RandomizedParams<C: Ciphersuite> {
256    /// The randomizer, also called α
257    randomizer: Randomizer<C>,
258    /// The generator multiplied by the randomizer.
259    randomizer_element: <C::Group as Group>::Element,
260    /// The randomized group public key. The group public key added to the randomizer element.
261    randomized_verifying_key: frost_core::VerifyingKey<C>,
262}
263
264impl<C> RandomizedParams<C>
265where
266    C: RandomizedCiphersuite,
267{
268    /// Create a new [`RandomizedParams`] for the given [`VerifyingKey`] and
269    /// the given `participants`.
270    #[cfg(feature = "serialization")]
271    pub fn new<R: RngCore + CryptoRng>(
272        group_verifying_key: &VerifyingKey<C>,
273        signing_package: &SigningPackage<C>,
274        rng: R,
275    ) -> Result<Self, Error<C>> {
276        Ok(Self::from_randomizer(
277            group_verifying_key,
278            Randomizer::new(rng, signing_package)?,
279        ))
280    }
281}
282
283impl<C> RandomizedParams<C>
284where
285    C: Ciphersuite,
286{
287    /// Create a new [`RandomizedParams`] for the given [`VerifyingKey`] and the
288    /// given `participants` for the  given `randomizer`. The `randomizer` MUST
289    /// be generated uniformly at random! Use [`RandomizedParams::new()`] which
290    /// generates a fresh randomizer, unless your application requires generating
291    /// a randomizer outside.
292    pub fn from_randomizer(
293        group_verifying_key: &VerifyingKey<C>,
294        randomizer: Randomizer<C>,
295    ) -> Self {
296        let randomizer_element = <C::Group as Group>::generator() * randomizer.to_scalar();
297        let verifying_key_element = group_verifying_key.to_element();
298        let randomized_verifying_key_element = verifying_key_element + randomizer_element;
299        let randomized_verifying_key = VerifyingKey::<C>::new(randomized_verifying_key_element);
300
301        Self {
302            randomizer,
303            randomizer_element,
304            randomized_verifying_key,
305        }
306    }
307}
308
309impl<C> core::fmt::Debug for RandomizedParams<C>
310where
311    C: Ciphersuite,
312{
313    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
314        f.debug_struct("RandomizedParams")
315            .field("randomizer", &self.randomizer)
316            .field(
317                "randomizer_element",
318                &<C::Group as Group>::serialize(&self.randomizer_element)
319                    .map(hex::encode)
320                    .unwrap_or("<invalid>".to_string()),
321            )
322            .field("randomized_verifying_key", &self.randomized_verifying_key)
323            .finish()
324    }
325}