Skip to main content

cggmp24/
key_share.rs

1//! Key share
2
3use std::ops;
4use std::sync::Arc;
5
6use generic_ec::{Curve, NonZero, Point};
7use paillier_zk::backend::Integer;
8use paillier_zk::paillier_encryption_in_range as π_enc;
9use serde::{Deserialize, Serialize};
10use thiserror::Error;
11
12use crate::security_level::SecurityLevel;
13
14#[doc(inline)]
15pub use cggmp24_keygen::key_share::{
16    CoreKeyShare as IncompleteKeyShare, DirtyCoreKeyShare as DirtyIncompleteKeyShare, DirtyKeyInfo,
17    HdError, InvalidCoreShare as InvalidIncompleteKeyShare, KeyInfo, Valid, Validate,
18    ValidateError, ValidateFromParts, VssSetup,
19};
20
21/// Key share
22///
23#[doc = include_str!("../docs/key_share.md")]
24///
25#[doc = include_str!("../docs/validated_key_share_note.md")]
26#[doc = include_str!("../docs/validated_key_share_disclaimer.md")]
27pub type KeyShare<E, L = crate::default_choice::SecurityLevel> = Valid<DirtyKeyShare<E, L>>;
28
29/// Auxiliary information
30pub type AuxInfo<L = crate::default_choice::SecurityLevel> = Valid<DirtyAuxInfo<L>>;
31
32/// Dirty aux info
33#[derive(Clone, Serialize, Deserialize)]
34#[serde(bound = "")]
35pub struct DirtyAuxInfo<L: SecurityLevel = crate::default_choice::SecurityLevel> {
36    /// Secret prime $p$
37    pub p: Integer,
38    /// Secret prime $q$
39    pub q: Integer,
40    /// Paillier public keys $\vec N = (N_j)_{j \in \[n\]}$
41    pub N: Vec<Integer>,
42    /// Parties Pedersen parameters
43    ///
44    /// `pedersen_params[i]` corresponds to pedersen params of $\ith$ party
45    ///
46    /// Note that although this is supposed to only contain public information, when CRT
47    /// optimization is enabled, it may contain sensitive information!
48    pub pedersen_params: Vec<PedersenParams>,
49    /// Security level that was used to generate aux info
50    #[serde(skip)]
51    pub security_level: std::marker::PhantomData<L>,
52}
53
54/// Dirty (unvalidated) key share
55///
56#[doc = include_str!("../docs/key_share.md")]
57#[derive(Clone, Serialize, Deserialize)]
58#[serde(bound = "")]
59pub struct DirtyKeyShare<E: Curve, L: SecurityLevel = crate::default_choice::SecurityLevel> {
60    /// Core key share
61    pub core: DirtyIncompleteKeyShare<E>,
62    /// Auxiliary info
63    pub aux: DirtyAuxInfo<L>,
64}
65
66/// Party pedersen parameters
67#[derive(Debug, Clone, Serialize, Deserialize)]
68#[serde(bound = "")]
69pub struct PedersenParams {
70    /// Defines Pedersen Ring modulo $\hat N_i$
71    pub hat_N: Integer,
72    /// Ring-Perdesten parameter $s_i$
73    pub s: Integer,
74    /// Ring-Perdesten parameter $t_i$
75    pub t: Integer,
76    /// Precomputed table for faster multiexponentiation
77    ///
78    /// It's used to compute $s_i^x t_i^y \bmod \hat N_i$ faster given $x,y$ in certain
79    /// range predefined by security level
80    #[serde(default)]
81    pub multiexp: Option<Arc<paillier_zk::multiexp::MultiexpTable>>,
82    /// Enables faster modular exponentiation when factorization of `N` is known
83    ///
84    /// Note that it is extreamly sensitive! Leaking `crt` exposes Paillier private key.
85    ///
86    /// It's used to compute $x^y \bmod \hat N_i$ faster
87    #[serde(default)]
88    pub crt: Option<paillier_zk::fast_paillier::utils::CrtExp>,
89}
90
91impl<L: SecurityLevel> Validate for DirtyAuxInfo<L> {
92    type Error = InvalidKeyShare;
93
94    fn is_valid(&self) -> Result<(), InvalidKeyShare> {
95        if self
96            .pedersen_params
97            .iter()
98            .any(|p| !p.s.gcd_ref(&p.hat_N).is_one() || !p.t.gcd_ref(&p.hat_N).is_one())
99        {
100            return Err(InvalidKeyShareReason::StGcdN.into());
101        }
102
103        if [&self.p, &self.q]
104            .iter()
105            .any(|prime| !crate::security_level::validate_secret_paillier_prime_size::<L>(prime))
106        {
107            return Err(InvalidKeyShareReason::PaillierSkTooSmall.into());
108        }
109
110        if let Some(N) = self
111            .N
112            .iter()
113            .find(|N| !crate::security_level::validate_public_paillier_key_size::<L>(N))
114        {
115            return Err(InvalidKeyShareReason::PaillierPkTooSmall {
116                required: L::RSA_PUBKEY_BITLEN,
117                actual: N.significant_bits(),
118            }
119            .into());
120        }
121
122        if let Some(params) = self.pedersen_params.iter().find(|params| {
123            !crate::security_level::validate_public_paillier_key_size::<L>(&params.hat_N)
124        }) {
125            return Err(InvalidKeyShareReason::PedersenModuleTooSmall {
126                required: L::RSA_PUBKEY_BITLEN,
127                actual: params.hat_N.significant_bits(),
128            }
129            .into());
130        }
131
132        Ok(())
133    }
134}
135
136impl<L: SecurityLevel> DirtyAuxInfo<L> {
137    /// Precomputes multiexponentiation tables
138    ///
139    /// Enables optimization that makes signing and presigning faster. Precomputation may take a while.
140    /// It noticebly increases size of aux data both in RAM and on disk (after serialization).
141    ///
142    /// Returns error if building a multiexp table failed. In this case, the key share stays unmodified.
143    /// On success, multiexp tables are saved into the key share (old tables, if present, are overwritten).
144    pub fn precompute_multiexp_tables(&mut self) -> Result<(), InvalidKeyShare> {
145        let (x_bits, y_bits) = crate::security_level::max_exponents_size::<L>();
146        let tables = self
147            .pedersen_params
148            .iter()
149            .map(|aux_i| {
150                paillier_zk::multiexp::MultiexpTable::build(
151                    &aux_i.s,
152                    &aux_i.t,
153                    x_bits,
154                    y_bits,
155                    aux_i.hat_N.clone(),
156                )
157                .map(Arc::new)
158            })
159            .collect::<Option<Vec<_>>>()
160            .ok_or(InvalidKeyShareReason::BuildMultiexpTable)?;
161        self.pedersen_params
162            .iter_mut()
163            .zip(tables)
164            .for_each(|(aux_i, table_i)| aux_i.multiexp = Some(table_i));
165        Ok(())
166    }
167
168    /// Returns size of all multiexp tables (in bytes) stored within key share
169    pub fn multiexp_tables_size(&self) -> usize {
170        self.pedersen_params
171            .iter()
172            .map(|aux_i| {
173                aux_i
174                    .multiexp
175                    .as_ref()
176                    .map(|t| t.size_in_bytes())
177                    .unwrap_or(0)
178            })
179            .sum()
180    }
181
182    /// Precomputes CRT parameters
183    ///
184    /// Refer to [`PedersenParams::precompute_crt`] for the docs.
185    pub fn precompute_crt(&mut self, i: u16) -> Result<(), InvalidKeyShare> {
186        let aux_i = self
187            .pedersen_params
188            .get_mut(usize::from(i))
189            .ok_or(InvalidKeyShareReason::CrtINotInRange)?;
190        aux_i.precompute_crt(&self.p, &self.q)
191    }
192}
193
194impl PedersenParams {
195    /// Precompute multiexponentiation table
196    ///
197    /// Enables optimization that makes signing and presigning faster. Precomputation may take a while.
198    /// It noticebly increases size of aux data both in RAM and on disk (after serialization).
199    ///
200    /// Returns error if building a multiexp table failed. On success, multiexp tables are saved (old
201    /// tables, if present, are overwritten).
202    ///
203    /// Note that provided security level must match the actual security level being used in the
204    /// protocol. Otherwise, optimization won't work, and it actually will make the protocol slower.
205    pub fn precompute_multiexp_table<L: SecurityLevel>(&mut self) -> Result<(), InvalidKeyShare> {
206        let (x_bits, y_bits) = crate::security_level::max_exponents_size::<L>();
207        let multiexp = paillier_zk::multiexp::MultiexpTable::build(
208            &self.s,
209            &self.t,
210            x_bits,
211            y_bits,
212            self.hat_N.clone(),
213        )
214        .map(Arc::new)
215        .ok_or(InvalidKeyShareReason::BuildMultiexpTable)?;
216        self.multiexp = Some(multiexp);
217        Ok(())
218    }
219
220    /// Precomputes CRT parameters
221    ///
222    /// Enables optimization of modular exponentiation in Zero-Knowledge proofs validation. Precomputation
223    /// should be relatively fast. It increases size of key share in RAM and on disk, but not noticeably.
224    ///
225    /// Takes primes `p`, `q` as input that correspond to signer Paillier secret key.
226    ///
227    /// Returns error if provided primes do not correspond to a Paillier secret key of the signer, or if
228    /// precomputation failed. On success, updates CRT params stored within the structure (old params, if
229    /// present, are overwritten)
230    ///
231    /// Note: CRT parameters contain secret information. Leaking them exposes secret Paillier key. Keep
232    /// them secret (as well as rest of the key share).
233    pub fn precompute_crt(&mut self, p: &Integer, q: &Integer) -> Result<(), InvalidKeyShare> {
234        if p * q != self.hat_N {
235            return Err(InvalidKeyShareReason::CrtInvalidPq.into());
236        }
237        let crt = paillier_zk::fast_paillier::utils::CrtExp::build_n(p, q)
238            .ok_or(InvalidKeyShareReason::BuildCrt)?;
239        self.crt = Some(crt);
240        Ok(())
241    }
242}
243
244impl<E: Curve, L: SecurityLevel> Validate for DirtyKeyShare<E, L> {
245    type Error = InvalidKeyShare;
246
247    fn is_valid(&self) -> Result<(), InvalidKeyShare> {
248        self.core.is_valid()?;
249        self.aux.is_valid()?;
250        Self::validate_consistency(&self.core, &self.aux)
251    }
252}
253
254impl<E: Curve, L: SecurityLevel> ValidateFromParts<(IncompleteKeyShare<E>, AuxInfo<L>)>
255    for DirtyKeyShare<E, L>
256{
257    fn validate_parts(
258        (core, aux): &(IncompleteKeyShare<E>, AuxInfo<L>),
259    ) -> Result<(), Self::Error> {
260        Self::validate_consistency(core, aux)
261    }
262
263    fn from_parts((core, aux): (IncompleteKeyShare<E>, AuxInfo<L>)) -> Self {
264        Self {
265            core: core.into_inner(),
266            aux: aux.into_inner(),
267        }
268    }
269}
270
271impl<E: Curve, L: SecurityLevel> DirtyKeyShare<E, L> {
272    /// Perform consistency check between core and aux
273    fn validate_consistency(
274        core: &DirtyIncompleteKeyShare<E>,
275        aux: &DirtyAuxInfo<L>,
276    ) -> Result<(), InvalidKeyShare> {
277        if core.public_shares.len() != aux.pedersen_params.len() {
278            return Err(InvalidKeyShareReason::AuxLen.into());
279        }
280
281        let N_i = &aux.N[usize::from(core.i)];
282        if *N_i != &aux.p * &aux.q {
283            return Err(InvalidKeyShareReason::PrimesMul.into());
284        }
285
286        Ok(())
287    }
288}
289
290impl<E: Curve> DirtyKeyShare<E> {
291    /// Precomputes CRT parameters
292    ///
293    /// Enables optimization of modular exponentiation in Zero-Knowledge proofs validation. Precomputation
294    /// should be relatively fast. It increases size of key share in RAM and on disk, but not noticeably.
295    ///
296    /// Returns error if precomputation failed. In this case, the key share stays unmodified. On success,
297    /// CRT parameters are saved into the key share (old params, if present, are overwritten)
298    ///
299    /// Note: CRT parameters contain secret information. Leaking them exposes secret Paillier key. Keep
300    /// them secret (as well as rest of the key share).
301    pub fn precompute_crt(&mut self) -> Result<(), InvalidKeyShare> {
302        let i = self.core.i;
303        self.aux.precompute_crt(i)
304    }
305}
306
307impl<E: Curve, L: SecurityLevel> AsRef<DirtyIncompleteKeyShare<E>> for DirtyKeyShare<E, L> {
308    fn as_ref(&self) -> &DirtyIncompleteKeyShare<E> {
309        &self.core
310    }
311}
312impl<E: Curve, L: SecurityLevel> AsRef<DirtyAuxInfo<L>> for DirtyKeyShare<E, L> {
313    fn as_ref(&self) -> &DirtyAuxInfo<L> {
314        &self.aux
315    }
316}
317
318impl<E: Curve, L: SecurityLevel> ops::Deref for DirtyKeyShare<E, L> {
319    type Target = DirtyIncompleteKeyShare<E>;
320
321    fn deref(&self) -> &Self::Target {
322        &self.core
323    }
324}
325
326/// Any (validated) key share
327///
328/// Implemented for both [KeyShare] and [IncompleteKeyShare]. Used in methods
329/// that accept both types of key shares, like [reconstruct_secret_key].
330pub trait AnyKeyShare<E: Curve>: AsRef<IncompleteKeyShare<E>> {
331    /// Returns amount of key co-holders
332    fn n(&self) -> u16 {
333        #[allow(clippy::expect_used)]
334        self.as_ref()
335            .public_shares
336            .len()
337            .try_into()
338            .expect("valid key share is guaranteed to have amount of signers fitting into u16")
339    }
340
341    /// Returns threshold
342    ///
343    /// Threshold is an amount of signers required to cooperate in order to sign a message
344    /// and/or generate presignature
345    fn min_signers(&self) -> u16 {
346        self.as_ref()
347            .vss_setup
348            .as_ref()
349            .map(|s| s.min_signers)
350            .unwrap_or_else(|| self.n())
351    }
352
353    /// Returns public key shared by signers
354    fn shared_public_key(&self) -> NonZero<Point<E>> {
355        self.as_ref().shared_public_key
356    }
357}
358
359impl<E: Curve, T: AsRef<IncompleteKeyShare<E>>> AnyKeyShare<E> for T {}
360
361/// Reconstructs a secret key from set of at least [`min_signers`](KeyShare::min_signers) key shares
362///
363/// Requires at least [`min_signers`](KeyShare::min_signers) distinct key shares from the same generation
364/// (key refresh produces key shares of the next generation). Accepts both [`KeyShare`] and [`IncompleteKeyShare`].
365/// Returns error if input is invalid.
366///
367/// Note that, normally, secret key is not supposed to be reconstructed, and key
368/// shares should never be at one place. This basically defeats purpose of MPC and
369/// creates single point of failure/trust.
370#[cfg(feature = "spof")]
371pub fn reconstruct_secret_key<E: Curve>(
372    key_shares: &[impl AnyKeyShare<E>],
373) -> Result<generic_ec::SecretScalar<E>, ReconstructError> {
374    cggmp24_keygen::key_share::reconstruct_secret_key(key_shares)
375}
376
377impl From<&PedersenParams> for π_enc::Aux {
378    fn from(aux: &PedersenParams) -> Self {
379        Self {
380            s: aux.s.clone(),
381            t: aux.t.clone(),
382            rsa_modulo: aux.hat_N.clone(),
383            multiexp: aux.multiexp.clone(),
384            crt: aux.crt.clone(),
385        }
386    }
387}
388
389/// Error indicating that key share is not valid
390#[derive(Debug, Error)]
391#[error(transparent)]
392pub struct InvalidKeyShare(#[from] InvalidKeyShareReason);
393
394#[derive(Debug, Error)]
395enum InvalidKeyShareReason {
396    #[error(transparent)]
397    InvalidCoreShare(InvalidIncompleteKeyShare),
398    #[error("size of parties auxiliary data list doesn't match `n`: n != parties.len()")]
399    AuxLen,
400    #[error("N_i != p q")]
401    PrimesMul,
402    #[error("gcd(s_j, N_j) != 1 or gcd(t_j, N_j) != 1")]
403    StGcdN,
404    #[error("paillier secret key doesn't match security level (primes are too small)")]
405    PaillierSkTooSmall,
406    #[error("paillier public key of one of the signers doesn't match security level: required bit length = {required}, actual = {actual}")]
407    PaillierPkTooSmall { required: u32, actual: u64 },
408    #[error("pedersen module of one of the signers doesn't match security level: required bit length = {required}, actual = {actual}")]
409    PedersenModuleTooSmall { required: u32, actual: u64 },
410    #[error("couldn't build a multiexp table")]
411    BuildMultiexpTable,
412    #[error("provided index `i` does not correspond to an index of the signer at key generation")]
413    CrtINotInRange,
414    #[error("provided primes `p`, `q` do not correspond to signer Paillier public key")]
415    CrtInvalidPq,
416    #[error("couldn't build CRT parameters")]
417    BuildCrt,
418}
419
420/// Error indicating that [key reconstruction](reconstruct_secret_key) failed
421#[cfg(feature = "spof")]
422pub use cggmp24_keygen::key_share::ReconstructError;
423
424impl From<InvalidIncompleteKeyShare> for InvalidKeyShare {
425    fn from(err: InvalidIncompleteKeyShare) -> Self {
426        Self(InvalidKeyShareReason::InvalidCoreShare(err))
427    }
428}
429
430impl<T> From<ValidateError<T, InvalidIncompleteKeyShare>> for InvalidKeyShare {
431    fn from(err: ValidateError<T, InvalidIncompleteKeyShare>) -> Self {
432        err.into_error().into()
433    }
434}
435
436impl<T> From<ValidateError<T, InvalidKeyShare>> for InvalidKeyShare {
437    fn from(err: cggmp24_keygen::key_share::ValidateError<T, InvalidKeyShare>) -> Self {
438        err.into_error()
439    }
440}
441
442/// Tools for migrating key shares from cggmp24 to cggmp24
443///
444/// CGGMP24 revision of the protocol introduced changes in the structure of the key shares:
445/// * The core key share [`IncompleteKeyShare`] has not changed, you can use cggmp24 library
446///   to deserialize core key share from cggmp24
447/// * However, [`AuxInfo`] and [`KeyShare`] have changed: now each signer has distinct Paillier
448///   and Pedersen keys (previously, they both were one key)
449///
450/// If you have [`KeyShare`]s serialized by cggmp24, we advise you to discard auxiliary data
451/// stored within the key share, extract the core key share (that contains the most important
452/// part of the key share), and re-generate auxiliary data by carrying out
453/// [`aux_info_gen`](crate::aux_info_gen) protocol.
454pub mod cggmp21_compat {
455    /// Deserializes a key share from cggmp24, discards the auxiliary data and extracts the core share.
456    #[derive(serde::Deserialize, Clone)]
457    pub struct ExtractCoreShare<E: generic_ec::Curve> {
458        /// Extracted core share
459        pub core: super::IncompleteKeyShare<E>,
460        #[serde(rename = "aux")]
461        _aux: serde::de::IgnoredAny,
462    }
463}