cggmp21 0.6.3

TSS ECDSA implementation based on CGGMP21 paper
Documentation
//! Trusted dealer
//!
//! Trusted dealer can be used to generate key shares in one place. Note
//! that it creates an SPOF/T (single point of failure/trust). Trusted
//! dealer is mainly intended to be used in tests, or it can be used to
//! import key into TSS.
//!
//! ## Example
//! Import a key into 3-out-of-5 TSS:
//! ```rust,no_run
//! # use rand::rngs::OsRng;
//! # let mut rng = OsRng;
//! use cggmp21::{supported_curves::Secp256k1, security_level::SecurityLevel128};
//! use cggmp21::generic_ec::{SecretScalar, NonZero};
//!
//! let secret_key_to_be_imported = NonZero::<SecretScalar<Secp256k1>>::random(&mut rng);
//!
//! let key_shares = cggmp21::trusted_dealer::builder::<Secp256k1, SecurityLevel128>(5)
//!     .set_threshold(Some(3))
//!     .set_shared_secret_key(secret_key_to_be_imported)
//!     .generate_shares(&mut rng)?;
//! # Ok::<_, cggmp21::trusted_dealer::TrustedDealerError>(())
//! ```

use std::{iter, marker::PhantomData};

use generic_ec::{Curve, NonZero, SecretScalar};
use paillier_zk::{
    rug::{Complete, Integer},
    IntegerExt,
};
use rand_core::{CryptoRng, RngCore};
use thiserror::Error;

use crate::{
    key_share::{
        AuxInfo, DirtyAuxInfo, IncompleteKeyShare, InvalidKeyShare, KeyShare, PartyAux, Validate,
    },
    security_level::SecurityLevel,
    utils,
};

/// Construct a trusted dealer builder
///
/// Takes amount of key shares `n` to be generated
///
/// Alias to [`TrustedDealerBuilder::new`]
pub fn builder<E: Curve, L: SecurityLevel>(n: u16) -> TrustedDealerBuilder<E, L> {
    TrustedDealerBuilder::new(n)
}

type CoreBuilder<E> = key_share::trusted_dealer::TrustedDealerBuilder<E>;

/// Trusted dealer builder
pub struct TrustedDealerBuilder<E: Curve, L: SecurityLevel> {
    inner: CoreBuilder<E>,
    n: u16,
    pregenerated_primes: Option<Vec<(Integer, Integer)>>,
    enable_mulitexp: bool,
    enable_crt: bool,
    _ph: PhantomData<L>,
}

impl<E: Curve, L: SecurityLevel> TrustedDealerBuilder<E, L> {
    /// Construct a trusted dealer builder
    ///
    /// Takes amount of key shares `n` to be generated
    pub fn new(n: u16) -> Self {
        TrustedDealerBuilder {
            inner: CoreBuilder::new(n),
            n,
            pregenerated_primes: None,
            enable_mulitexp: false,
            enable_crt: false,
            _ph: PhantomData,
        }
    }

    /// Sets threshold value
    ///
    /// If threshold is `Some(_)`, resulting key shares will be generated
    /// using t-out-of-n VSS scheme. If it's `None`, trusted dealer will
    /// generate additive key shares in n-out-ouf-n scheme.
    ///
    /// Note that setting `t=Some(n)` is not the same as setting `t=None`.
    /// Both produce n-out-of-n key shares, but `t=Some(n)` mocks threshold
    /// key generation with `threshold=n`, `t=None` mock non-threshold key
    /// generation.
    ///
    /// Default: `None`
    pub fn set_threshold(self, t: Option<u16>) -> Self {
        Self {
            inner: self.inner.set_threshold(t),
            ..self
        }
    }

    /// Sets shared secret key to be generated
    ///
    /// Resulting key shares will share specified secret key.
    pub fn set_shared_secret_key(self, sk: NonZero<SecretScalar<E>>) -> Self {
        Self {
            inner: self.inner.set_shared_secret_key(sk),
            ..self
        }
    }

    /// Sets pregenerated primes to use
    ///
    /// `primes` should have exactly `n` pairs of primes.
    pub fn set_pregenerated_primes(self, primes: Vec<(Integer, Integer)>) -> Self {
        Self {
            pregenerated_primes: Some(primes),
            ..self
        }
    }

    /// Enables multiexp optimization
    ///
    /// It takes additional time to precompute multiexp tables, and it makes the key shares larger,
    /// but it makes the signing and presignature generation protocols faster
    pub fn enable_multiexp(self, v: bool) -> Self {
        Self {
            enable_mulitexp: v,
            ..self
        }
    }

    /// Enables CRT optimization
    ///
    /// CRT optimization makes ZK proofs verification faster, and by doing so it makes the overall performance better
    pub fn enable_crt(self, v: bool) -> Self {
        Self {
            enable_crt: v,
            ..self
        }
    }

    /// Specifies that the key being generated shall support HD derivation
    #[cfg(feature = "hd-wallet")]
    pub fn hd_wallet(self, v: bool) -> Self {
        Self {
            inner: self.inner.hd_wallet(v),
            ..self
        }
    }

    /// Generates [`IncompleteKeyShare`]s
    ///
    /// For Shamir secret sharing, it's shared at points `1` to `n`
    ///
    /// Returns error if provided inputs are invalid, or if internal
    /// error has occurred.
    pub fn generate_core_shares(
        self,
        rng: &mut (impl RngCore + CryptoRng),
    ) -> Result<Vec<IncompleteKeyShare<E>>, TrustedDealerError> {
        self.inner
            .generate_shares(rng)
            .map_err(Reason::CoreError)
            .map_err(TrustedDealerError)
    }

    /// Generates [`KeyShare`]s
    ///
    /// For Shamir secret sharing, it's shared at points `1` to `n`
    ///
    /// Returns error if provided inputs are invalid, or if internal
    /// error has occurred.
    pub fn generate_shares(
        self,
        rng: &mut (impl RngCore + CryptoRng),
    ) -> Result<Vec<KeyShare<E, L>>, TrustedDealerError> {
        self.generate_shares_at_internal(
            key_share::trusted_dealer::TrustedDealerBuilder::generate_shares,
            rng,
        )
    }

    /// Generates [`KeyShare`]s shared at preimages provided. Each share is
    /// going to have the given `preimages` as its `I` component.
    ///
    /// Preimages are ignored for additive key shares.
    ///
    /// Returns error if provided inputs are invalid, or if internal
    /// error has occurred.
    pub fn generate_shares_at(
        self,
        preimages: Vec<NonZero<generic_ec::Scalar<E>>>,
        rng: &mut (impl RngCore + CryptoRng),
    ) -> Result<Vec<KeyShare<E, L>>, TrustedDealerError> {
        self.generate_shares_at_internal(
            |builder, rng| builder.generate_shares_at(preimages, rng),
            rng,
        )
    }

    /// Generates [`KeyShare`]s shared at random points
    ///
    /// Returns error if provided inputs are invalid, or if internal
    /// error has occurred.
    ///
    /// For Shamir secret sharing, the points at which the value is shared at
    /// are chosen at random between `1` and `u16::MAX`. For additive shares,
    /// this is the same as [`TrustedDealerBuilder::generate_shares`]
    ///
    /// Returns error if provided inputs are invalid, or if internal
    /// error has occurred.
    pub fn generate_shares_at_random(
        self,
        rng: &mut (impl rand_core::RngCore + rand_core::CryptoRng),
    ) -> Result<Vec<KeyShare<E, L>>, TrustedDealerError> {
        self.generate_shares_at_internal(
            key_share::trusted_dealer::TrustedDealerBuilder::generate_shares_at_random,
            rng,
        )
    }

    fn generate_shares_at_internal<R, F>(
        mut self,
        inner_generate: F,
        rng: &mut R,
    ) -> Result<Vec<KeyShare<E, L>>, TrustedDealerError>
    where
        F: FnOnce(
            CoreBuilder<E>,
            &mut R,
        ) -> Result<
            Vec<IncompleteKeyShare<E>>,
            key_share::trusted_dealer::TrustedDealerError,
        >,
        R: rand_core::RngCore + rand_core::CryptoRng,
    {
        let n = self.n;
        let enable_multiexp = self.enable_mulitexp;
        let enable_crt = self.enable_crt;

        let primes = self.pregenerated_primes.take();
        let core_key_shares = inner_generate(self.inner, rng).map_err(Reason::CoreError)?;
        let aux_data = if let Some(primes) = primes {
            generate_aux_data_with_primes(rng, primes, enable_multiexp, enable_crt)?
        } else {
            generate_aux_data(rng, n, enable_multiexp, enable_crt)?
        };

        let key_shares = core_key_shares
            .into_iter()
            .zip(aux_data)
            .map(|(core, aux)| KeyShare::from_parts((core, aux)))
            .collect::<Result<Vec<_>, _>>()
            .map_err(|err| Reason::InvalidKeyShare(err.into_error()))?;

        Ok(key_shares)
    }
}

/// Generates auxiliary data for `n` signers
///
/// Auxiliary data can be used to "complete" core key share using [`KeyShare::from_parts`] constructor.
///
/// `enable_multiexp` and `enable_crt` flags configure whether to enable [multiexp](TrustedDealerBuilder::enable_multiexp)
/// and [CRT](TrustedDealerBuilder::enable_crt) optimizations.
pub fn generate_aux_data<L: SecurityLevel, R: RngCore + CryptoRng>(
    rng: &mut R,
    n: u16,
    enable_multiexp: bool,
    enable_crt: bool,
) -> Result<Vec<AuxInfo<L>>, TrustedDealerError> {
    let primes =
        iter::repeat_with(|| crate::key_refresh::PregeneratedPrimes::<L>::generate(rng).split())
            .take(n.into())
            .collect::<Vec<_>>();

    generate_aux_data_with_primes(rng, primes, enable_multiexp, enable_crt)
}

/// Generates auxiliary data for `n` signers using provided pregenerated primes
///
/// `pregenerated_primes` should have exactly `n` pairs of primes.
///
/// `enable_multiexp` and `enable_crt` flags configure whether to enable [multiexp](TrustedDealerBuilder::enable_multiexp)
/// and [CRT](TrustedDealerBuilder::enable_crt) optimizations.
pub fn generate_aux_data_with_primes<L: SecurityLevel, R: RngCore + CryptoRng>(
    rng: &mut R,
    pregenerated_primes: Vec<(Integer, Integer)>,
    enable_multiexp: bool,
    enable_crt: bool,
) -> Result<Vec<AuxInfo<L>>, TrustedDealerError> {
    let public_aux_data = pregenerated_primes
        .iter()
        .map(|(p, q)| {
            let N = (p * q).complete();

            let φ_N = (p - 1u8).complete() * (q - 1u8).complete();

            let r = Integer::gen_invertible(&N, rng);
            let λ = φ_N.random_below_ref(&mut utils::external_rand(rng)).into();

            let t = r.square().modulo(&N);
            let s = t.pow_mod_ref(&λ, &N).ok_or(Reason::PowMod)?.into();

            let mut aux = PartyAux {
                N,
                s,
                t,
                multiexp: None,
                crt: None,
            };
            if enable_multiexp {
                aux.precompute_multiexp_table::<L>()
                    .map_err(Reason::BuildMultiexp)?;
            }
            Ok(aux)
        })
        .collect::<Result<Vec<_>, Reason>>()?;

    pregenerated_primes
        .into_iter()
        .enumerate()
        .map(|(i, (p, q))| {
            let mut public_aux_data = public_aux_data.clone();
            if enable_crt {
                public_aux_data[i]
                    .precompute_crt(&p, &q)
                    .map_err(Reason::BuildCrt)?;
            }

            DirtyAuxInfo {
                p,
                q,
                parties: public_aux_data,
                security_level: PhantomData,
            }
            .validate()
            .map_err(|err| Reason::InvalidKeyShare(err.into_error()))
        })
        .collect::<Result<Vec<_>, _>>()
        .map_err(TrustedDealerError)
}

/// Error explaining why trusted dealer failed to generate shares
#[derive(Debug, Error)]
#[error(transparent)]
pub struct TrustedDealerError(#[from] Reason);

#[derive(Debug, Error)]
enum Reason {
    #[error("trusted dealer failed to generate shares due to internal error")]
    InvalidKeyShare(#[source] InvalidKeyShare),
    #[error("pow mod undefined")]
    PowMod,
    #[error("couldn't build a CRT")]
    BuildCrt(#[source] InvalidKeyShare),
    #[error("couldn't build multiexp tables")]
    BuildMultiexp(#[source] InvalidKeyShare),
    #[error(transparent)]
    CoreError(#[from] key_share::trusted_dealer::TrustedDealerError),
}