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    /// Returns error if provided inputs are invalid, or if internal
99    /// error has occurred.
100    ///
101    /// For Shamir secret sharing, it's shared at points `1` to `n`
102    ///
103    /// Returns error if provided inputs are invalid, or if internal
104    /// error has occurred.
105    pub fn generate_shares(
106        self,
107        rng: &mut (impl rand_core::RngCore + rand_core::CryptoRng),
108    ) -> Result<Vec<CoreKeyShare<E>>, TrustedDealerError> {
109        let key_shares_indexes = (1..=self.n)
110            .map(|i| generic_ec::NonZero::from_scalar(Scalar::from(i)))
111            .collect::<Option<Vec<_>>>()
112            .ok_or(Reason::DeriveKeyShareIndex)?;
113        self.generate_shares_at(key_shares_indexes, rng)
114    }
115
116    /// Generates [`CoreKeyShare`]s shared at random points
117    ///
118    /// Returns error if provided inputs are invalid, or if internal
119    /// error has occurred.
120    ///
121    /// For Shamir secret sharing, the points at which the value is shared at
122    /// are chosen at random between `1` and `u16::MAX`. For additive shares,
123    /// this is the same as [`TrustedDealerBuilder::generate_shares`]
124    ///
125    /// Returns error if provided inputs are invalid, or if internal
126    /// error has occurred.
127    pub fn generate_shares_at_random(
128        self,
129        rng: &mut (impl rand_core::RngCore + rand_core::CryptoRng),
130    ) -> Result<Vec<CoreKeyShare<E>>, TrustedDealerError> {
131        // The chance of scalars repeating is negligible for usual fields in EC.
132        // But in any case the dupliactes are checked during the validation of
133        // CoreKeyShare
134        let points = (0..self.n)
135            .map(|_| generic_ec::NonZero::<Scalar<E>>::random(rng))
136            .collect();
137        self.generate_shares_at(points, rng)
138    }
139
140    /// Generates [`CoreKeyShare`]s shared at preimages provided. Each share is
141    /// going to have the given `preimages` as its `I` component.
142    ///
143    /// Preimages are ignored for additive key shares.
144    ///
145    /// Returns error if provided inputs are invalid, or if internal
146    /// error has occurred.
147    pub fn generate_shares_at(
148        self,
149        preimages: Vec<NonZero<Scalar<E>>>,
150        rng: &mut (impl rand_core::RngCore + rand_core::CryptoRng),
151    ) -> Result<Vec<CoreKeyShare<E>>, TrustedDealerError> {
152        if preimages.len() != usize::from(self.n) {
153            return Err(Reason::InvalidPreimages.into());
154        }
155
156        let shared_secret_key = self
157            .shared_secret_key
158            .unwrap_or_else(|| NonZero::<SecretScalar<_>>::random(rng));
159        let shared_public_key = Point::generator() * &shared_secret_key;
160        let secret_shares = if let Some(t) = self.t {
161            let f = generic_ec_zkp::polynomial::Polynomial::sample_with_const_term(
162                rng,
163                usize::from(t) - 1,
164                shared_secret_key,
165            );
166            debug_assert_eq!(
167                shared_public_key,
168                Point::generator() * f.value::<_, Scalar<_>>(&Scalar::zero())
169            );
170
171            preimages
172                .iter()
173                .map(|I_i| f.value(I_i))
174                .map(|mut x_i| SecretScalar::new(&mut x_i))
175                .map(|x| NonZero::from_secret_scalar(x).ok_or(Reason::ZeroShare))
176                .collect::<Result<Vec<_>, _>>()?
177        } else {
178            let mut shares = core::iter::repeat_with(|| NonZero::<SecretScalar<E>>::random(rng))
179                .take((self.n - 1).into())
180                .collect::<Vec<_>>();
181            shares.push(
182                NonZero::from_secret_scalar(SecretScalar::new(
183                    &mut (shared_secret_key - shares.iter().sum::<SecretScalar<E>>()),
184                ))
185                .ok_or(Reason::ZeroShare)?,
186            );
187            debug_assert_eq!(
188                shared_public_key,
189                shares.iter().sum::<SecretScalar<E>>() * Point::generator()
190            );
191            shares
192        };
193
194        let public_shares = secret_shares
195            .iter()
196            .map(|s_i| Point::generator() * s_i)
197            .collect::<Vec<_>>();
198
199        let vss_setup = self.t.map(|t| VssSetup {
200            min_signers: t,
201            I: preimages,
202        });
203
204        #[cfg(feature = "hd-wallet")]
205        let chain_code = if self.enable_hd {
206            let mut code = hd_wallet::ChainCode::default();
207            rng.fill_bytes(&mut code);
208            Some(code)
209        } else {
210            None
211        };
212
213        Ok((0u16..)
214            .zip(secret_shares)
215            .map(|(i, x_i)| {
216                crate::Validate::validate(crate::DirtyCoreKeyShare::<E> {
217                    i,
218                    key_info: crate::DirtyKeyInfo {
219                        curve: Default::default(),
220                        shared_public_key,
221                        public_shares: public_shares.clone(),
222                        vss_setup: vss_setup.clone(),
223                        #[cfg(feature = "hd-wallet")]
224                        chain_code,
225                    },
226                    x: x_i,
227                })
228                .map_err(|err| Reason::InvalidKeyShare(err.into_error()))
229            })
230            .collect::<Result<Vec<_>, _>>()?)
231    }
232}
233
234/// Error explaining why trusted dealer failed to generate shares
235#[derive(Debug, displaydoc::Display)]
236#[cfg_attr(feature = "std", derive(thiserror::Error))]
237#[displaydoc("trusted dealer failed to generate shares")]
238pub struct TrustedDealerError(#[cfg_attr(feature = "std", source)] Reason);
239
240#[derive(Debug, displaydoc::Display)]
241#[cfg_attr(feature = "std", derive(thiserror::Error))]
242enum Reason {
243    #[displaydoc("trusted dealer failed to generate shares due to internal error")]
244    InvalidKeyShare(#[cfg_attr(feature = "std", source)] crate::InvalidCoreShare),
245    #[displaydoc("deriving key share index failed")]
246    DeriveKeyShareIndex,
247    #[displaydoc("randomly generated share is zero - probability of that is negligible")]
248    ZeroShare,
249    #[displaydoc("invalid share preimages given")]
250    InvalidPreimages,
251}
252
253impl From<Reason> for TrustedDealerError {
254    fn from(err: Reason) -> Self {
255        Self(err)
256    }
257}