Skip to main content

key_share/
trusted_dealer.rs

1//! Trusted dealer
2//!
3//! Trusted dealer can be used to generate key shares in one place. Note
4//! that it creates an SPOF/T (single point of failure/trust). Trusted
5//! dealer is mainly intended to be used in tests.
6//!
7//! ## Example
8//! Import a key into 3-out-of-5 TSS:
9//! ```rust,no_run
10//! # use rand_core::OsRng;
11//! # let mut rng = OsRng;
12//! use generic_ec::{curves::Secp256k1, SecretScalar, NonZero};
13//!
14//! let secret_key_to_be_imported = NonZero::<SecretScalar<Secp256k1>>::random(&mut rng);
15//!
16//! let key_shares = key_share::trusted_dealer::builder::<Secp256k1>(5)
17//!     .set_threshold(Some(3))
18//!     .set_shared_secret_key(secret_key_to_be_imported)
19//!     .generate_shares(&mut rng)?;
20//! # Ok::<_, key_share::trusted_dealer::TrustedDealerError>(())
21//! ```
22
23use alloc::vec::Vec;
24
25use generic_ec::{Curve, NonZero, Point, Scalar, SecretScalar};
26
27use crate::{CoreKeyShare, VssSetup};
28
29/// Construct a trusted dealer builder
30///
31/// Takes amount of key shares `n` to be generated
32///
33/// Alias to [`TrustedDealerBuilder::new`]
34pub fn builder<E: Curve>(n: u16) -> TrustedDealerBuilder<E> {
35    TrustedDealerBuilder::new(n)
36}
37
38/// Trusted dealer builder
39pub struct TrustedDealerBuilder<E: Curve> {
40    t: Option<u16>,
41    n: u16,
42    shared_secret_key: Option<NonZero<SecretScalar<E>>>,
43    #[cfg(feature = "hd-wallet")]
44    enable_hd: bool,
45}
46
47impl<E: Curve> TrustedDealerBuilder<E> {
48    /// Construct a trusted dealer builder
49    ///
50    /// Takes amount of key shares `n` to be generated
51    pub fn new(n: u16) -> Self {
52        TrustedDealerBuilder {
53            t: None,
54            n,
55            shared_secret_key: None,
56            #[cfg(feature = "hd-wallet")]
57            enable_hd: true,
58        }
59    }
60
61    /// Sets threshold value
62    ///
63    /// If threshold is `Some(_)`, resulting key shares will be generated
64    /// using t-out-of-n VSS scheme. If it's `None`, trusted dealer will
65    /// generate additive key shares in n-out-ouf-n scheme.
66    ///
67    /// Note that setting `t=Some(n)` is not the same as setting `t=None`.
68    /// Both produce n-out-of-n key shares, but `t=Some(n)` mocks threshold
69    /// key generation with `threshold=n`, `t=None` mock non-threshold key
70    /// generation.
71    ///
72    /// Default: `None`
73    pub fn set_threshold(self, t: Option<u16>) -> Self {
74        Self { t, ..self }
75    }
76
77    /// Sets shared secret key to be generated
78    ///
79    /// Resulting key shares will share specified secret key.
80    pub fn set_shared_secret_key(self, sk: NonZero<SecretScalar<E>>) -> Self {
81        Self {
82            shared_secret_key: Some(sk),
83            ..self
84        }
85    }
86
87    /// Specifies that the key being generated shall support HD derivation
88    #[cfg(feature = "hd-wallet")]
89    pub fn hd_wallet(self, v: bool) -> Self {
90        Self {
91            enable_hd: v,
92            ..self
93        }
94    }
95
96    /// Generates [`CoreKeyShare`]s
97    ///
98    /// For Shamir secret sharing, it's shared at points `1` to `n`
99    ///
100    /// Returns error if provided inputs are invalid, or if internal
101    /// error has occurred.
102    pub fn generate_shares(
103        self,
104        rng: &mut (impl rand_core::RngCore + rand_core::CryptoRng),
105    ) -> Result<Vec<CoreKeyShare<E>>, TrustedDealerError> {
106        let key_shares_indexes = (1..=self.n)
107            .map(|i| generic_ec::NonZero::from_scalar(Scalar::from(i)))
108            .collect::<Option<Vec<_>>>()
109            .ok_or(Reason::DeriveKeyShareIndex)?;
110        self.generate_shares_at(key_shares_indexes, rng)
111    }
112
113    /// Generates [`CoreKeyShare`]s shared at random points
114    ///
115    /// For Shamir secret sharing, the points at which the value is shared at
116    /// are chosen at random between `1` and `u16::MAX`. For additive shares,
117    /// this is the same as [`TrustedDealerBuilder::generate_shares`]
118    ///
119    /// Returns error if provided inputs are invalid, or if internal
120    /// error has occurred.
121    pub fn generate_shares_at_random(
122        self,
123        rng: &mut (impl rand_core::RngCore + rand_core::CryptoRng),
124    ) -> Result<Vec<CoreKeyShare<E>>, TrustedDealerError> {
125        // The chance of scalars repeating is negligible for usual fields in EC.
126        // But in any case the dupliactes are checked during the validation of
127        // CoreKeyShare
128        let points = (0..self.n)
129            .map(|_| generic_ec::NonZero::<Scalar<E>>::random(rng))
130            .collect();
131        self.generate_shares_at(points, rng)
132    }
133
134    /// Generates [`CoreKeyShare`]s shared at preimages provided. Each share is
135    /// going to have the given `preimages` as its `I` component.
136    ///
137    /// Preimages are ignored for additive key shares.
138    ///
139    /// Returns error if provided inputs are invalid, or if internal
140    /// error has occurred.
141    pub fn generate_shares_at(
142        self,
143        preimages: Vec<NonZero<Scalar<E>>>,
144        rng: &mut (impl rand_core::RngCore + rand_core::CryptoRng),
145    ) -> Result<Vec<CoreKeyShare<E>>, TrustedDealerError> {
146        if preimages.len() != usize::from(self.n) {
147            return Err(Reason::InvalidPreimages.into());
148        }
149
150        let shared_secret_key = self
151            .shared_secret_key
152            .unwrap_or_else(|| NonZero::<SecretScalar<_>>::random(rng));
153        let shared_public_key = Point::generator() * &shared_secret_key;
154        let secret_shares = if let Some(t) = self.t {
155            let f = generic_ec_zkp::polynomial::Polynomial::sample_with_const_term(
156                rng,
157                usize::from(t) - 1,
158                shared_secret_key,
159            );
160            debug_assert_eq!(
161                shared_public_key,
162                Point::generator() * f.value::<_, Scalar<_>>(&Scalar::zero())
163            );
164
165            preimages
166                .iter()
167                .map(|I_i| f.value(I_i))
168                .map(|mut x_i| SecretScalar::new(&mut x_i))
169                .map(|x| NonZero::from_secret_scalar(x).ok_or(Reason::ZeroShare))
170                .collect::<Result<Vec<_>, _>>()?
171        } else {
172            let mut shares = core::iter::repeat_with(|| NonZero::<SecretScalar<E>>::random(rng))
173                .take((self.n - 1).into())
174                .collect::<Vec<_>>();
175            shares.push(
176                NonZero::from_secret_scalar(SecretScalar::new(
177                    &mut (shared_secret_key - shares.iter().sum::<SecretScalar<E>>()),
178                ))
179                .ok_or(Reason::ZeroShare)?,
180            );
181            debug_assert_eq!(
182                shared_public_key,
183                shares.iter().sum::<SecretScalar<E>>() * Point::generator()
184            );
185            shares
186        };
187
188        let public_shares = secret_shares
189            .iter()
190            .map(|s_i| Point::generator() * s_i)
191            .collect::<Vec<_>>();
192
193        let vss_setup = self.t.map(|t| VssSetup {
194            min_signers: t,
195            I: preimages,
196        });
197
198        #[cfg(feature = "hd-wallet")]
199        let chain_code = if self.enable_hd {
200            let mut code = hd_wallet::ChainCode::default();
201            rng.fill_bytes(&mut code);
202            Some(code)
203        } else {
204            None
205        };
206
207        Ok((0u16..)
208            .zip(secret_shares)
209            .map(|(i, x_i)| {
210                crate::Validate::validate(crate::DirtyCoreKeyShare::<E> {
211                    i,
212                    key_info: crate::DirtyKeyInfo {
213                        curve: Default::default(),
214                        shared_public_key,
215                        public_shares: public_shares.clone(),
216                        vss_setup: vss_setup.clone(),
217                        #[cfg(feature = "hd-wallet")]
218                        chain_code,
219                    },
220                    x: x_i,
221                })
222                .map_err(|err| Reason::InvalidKeyShare(err.into_error()))
223            })
224            .collect::<Result<Vec<_>, _>>()?)
225    }
226}
227
228/// Error explaining why trusted dealer failed to generate shares
229#[derive(Debug, displaydoc::Display)]
230#[cfg_attr(feature = "std", derive(thiserror::Error))]
231#[displaydoc("trusted dealer failed to generate shares")]
232pub struct TrustedDealerError(#[cfg_attr(feature = "std", source)] Reason);
233
234#[derive(Debug, displaydoc::Display)]
235#[cfg_attr(feature = "std", derive(thiserror::Error))]
236enum Reason {
237    #[displaydoc("trusted dealer failed to generate shares due to internal error")]
238    InvalidKeyShare(#[cfg_attr(feature = "std", source)] crate::InvalidCoreShare),
239    #[displaydoc("deriving key share index failed")]
240    DeriveKeyShareIndex,
241    #[displaydoc("randomly generated share is zero - probability of that is negligible")]
242    ZeroShare,
243    #[displaydoc("invalid share preimages given")]
244    InvalidPreimages,
245}
246
247impl From<Reason> for TrustedDealerError {
248    fn from(err: Reason) -> Self {
249        Self(err)
250    }
251}