1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/*
    Copyright Michael Lodder. All Rights Reserved.
    SPDX-License-Identifier: Apache-2.0
*/

use crate::{Error, FeldmanVerifier, PedersenVerifier, Shamir, Share};
use core::marker::PhantomData;
use ff::PrimeField;
use group::{Group, GroupEncoding, ScalarMul};
use rand_chacha::ChaChaRng;
use rand_core::{CryptoRng, RngCore, SeedableRng};

/// Result from calling Pedersen::split_secret
#[derive(Clone, Debug)]
pub struct PedersenResult<
    F: PrimeField,
    G: Group + GroupEncoding + ScalarMul<F>,
    const S: usize,
    const T: usize,
    const N: usize,
> {
    /// The random blinding factor randomly generated or supplied
    pub blinding: F,
    /// The blinding shares
    pub blind_shares: [Share<S>; N],
    /// The secret shares
    pub secret_shares: [Share<S>; N],
    /// The verifier for validating shares
    pub verifier: PedersenVerifier<F, G, T>,
}

/// Pedersen's Verifiable secret sharing scheme.
/// (see <https://www.cs.cornell.edu/courses/cs754/2001fa/129.PDF>)
///
/// Pedersen provides a single method to split a secret and return the verifiers and shares.
/// To combine, use Shamir::combine_shares or Shamir::combine_shares_group.
///
/// Pedersen returns both Pedersen verifiers and Feldman verifiers for the purpose
/// that both may be needed for other protocols like Gennaro's DKG. Otherwise,
/// the Feldman verifiers may be discarded.
#[derive(Copy, Clone, Debug)]
pub struct Pedersen<const T: usize, const N: usize>;

impl<const T: usize, const N: usize> Pedersen<T, N> {
    /// Create shares from a secret.
    /// F is the prime field
    /// S is the number of bytes used to represent F.
    /// `blinding` is the blinding factor.
    /// If [`None`], a random value is generated in F.
    /// `share_generator` is the generator point to use for shares.
    /// If [`None`], the default generator is used.
    /// `blind_factor_generator` is the generator point to use for blinding factor shares.
    /// If [`None`], a random generator is used
    pub fn split_secret<F, G, R, const S: usize>(
        secret: F,
        blinding: Option<F>,
        share_generator: Option<G>,
        blind_factor_generator: Option<G>,
        rng: &mut R,
    ) -> Result<PedersenResult<F, G, S, T, N>, Error>
    where
        F: PrimeField,
        G: Group + GroupEncoding + Default + ScalarMul<F>,
        R: RngCore + CryptoRng,
    {
        Shamir::<T, N>::check_params(Some(secret))?;

        let mut seed = [0u8; 32];
        rng.fill_bytes(&mut seed);
        let mut crng = ChaChaRng::from_seed(seed);

        let g = share_generator.unwrap_or_else(G::generator);
        let t = F::random(&mut crng);
        let h = blind_factor_generator.unwrap_or_else(|| G::generator() * t);

        let blinding = blinding.unwrap_or_else(|| F::random(&mut crng));
        let (secret_shares, secret_polynomial) =
            Shamir::<T, N>::get_shares_and_polynomial(secret, &mut crng);
        let (blind_shares, blinding_polynomial) =
            Shamir::<T, N>::get_shares_and_polynomial(blinding, &mut crng);

        let mut feldman_commitments = [G::default(); T];
        let mut pedersen_commitments = [G::default(); T];
        // {(g^p0 h^r0), (g^p1, h^r1), ..., (g^pn, h^rn)}
        for i in 0..T {
            let g_i = g * secret_polynomial.coefficients[i];
            let h_i = h * blinding_polynomial.coefficients[i];
            feldman_commitments[i] = g_i;
            pedersen_commitments[i] = g_i + h_i;
        }
        Ok(PedersenResult {
            blinding,
            blind_shares,
            secret_shares,
            verifier: PedersenVerifier {
                generator: h,
                commitments: pedersen_commitments,
                feldman_verifier: FeldmanVerifier {
                    generator: g,
                    commitments: feldman_commitments,
                    marker: PhantomData,
                },
            },
        })
    }

    /// Reconstruct a secret from shares created from `split_secret`.
    /// The X-coordinates operate in `F`
    /// The Y-coordinates operate in `F`
    pub fn combine_shares<F, const S: usize>(shares: &[Share<S>]) -> Result<F, Error>
    where
        F: PrimeField,
    {
        Shamir::<T, N>::combine_shares::<F, S>(shares)
    }

    /// Reconstruct a secret from shares created from `split_secret`.
    /// The X-coordinates operate in `F`
    /// The Y-coordinates operate in `G`
    ///
    /// Exists to support operations like threshold BLS where the shares
    /// operate in `F` but the partial signatures operate in `G`.
    pub fn combine_shares_group<F, G, const S: usize>(shares: &[Share<S>]) -> Result<G, Error>
    where
        F: PrimeField,
        G: Group + GroupEncoding + ScalarMul<F> + Default,
    {
        Shamir::<T, N>::combine_shares_group::<F, G, S>(shares)
    }
}