commonware_cryptography/bls12381/dkg/
ops.rs

1//! Stateless operations useful in a DKG/Resharing procedure.
2
3use crate::bls12381::{
4    dkg::Error,
5    primitives::{group::Share, poly},
6};
7use rand::RngCore;
8use rayon::{prelude::*, ThreadPoolBuilder};
9use std::collections::BTreeMap;
10
11/// Generate shares and a commitment.
12pub fn generate_shares<R: RngCore>(
13    rng: &mut R,
14    share: Option<Share>,
15    n: u32,
16    t: u32,
17) -> (poly::Public, Vec<Share>) {
18    // Generate a secret polynomial and commit to it
19    let mut secret = poly::new_from(t - 1, rng);
20    if let Some(share) = share {
21        // Set the free coefficient of the secret polynomial to the secret
22        // of the previous DKG
23        secret.set(0, share.private);
24    }
25
26    // Commit to polynomial and generate shares
27    let commitment = poly::Public::commit(secret.clone());
28    let shares = (0..n)
29        .map(|i| {
30            let eval = secret.evaluate(i);
31            Share {
32                index: eval.index,
33                private: eval.value,
34            }
35        })
36        .collect::<Vec<_>>();
37    (commitment, shares)
38}
39
40/// Verify that a given commitment is valid for a dealer. If a previous
41/// polynomial is provided, verify that the commitment is on that polynomial.
42pub fn verify_commitment(
43    previous: Option<&poly::Public>,
44    dealer: u32,
45    commitment: &poly::Public,
46    t: u32,
47) -> Result<(), Error> {
48    if let Some(previous) = previous {
49        let expected = previous.evaluate(dealer).value;
50        if expected != *commitment.constant() {
51            return Err(Error::UnexpectedPolynomial);
52        }
53    }
54    if commitment.degree() != t - 1 {
55        return Err(Error::CommitmentWrongDegree);
56    }
57    Ok(())
58}
59
60/// Verify that a given share is valid for a specified recipient.
61pub fn verify_share(
62    previous: Option<&poly::Public>,
63    dealer: u32,
64    commitment: &poly::Public,
65    t: u32,
66    recipient: u32,
67    share: &Share,
68) -> Result<(), Error> {
69    // Verify that commitment is on previous public polynomial (if provided)
70    verify_commitment(previous, dealer, commitment, t)?;
71
72    // Check if share is valid
73    if share.index != recipient {
74        return Err(Error::MisdirectedShare);
75    }
76    let expected = share.public();
77    let given = commitment.evaluate(share.index);
78    if given.value != expected {
79        return Err(Error::ShareWrongCommitment);
80    }
81    Ok(())
82}
83
84/// Construct a new public polynomial by summing all commitments.
85pub fn construct_public(
86    commitments: Vec<poly::Public>,
87    required: u32,
88) -> Result<poly::Public, Error> {
89    if commitments.len() < required as usize {
90        return Err(Error::InsufficientDealings);
91    }
92    let mut public = poly::Public::zero();
93    for commitment in commitments {
94        public.add(&commitment);
95    }
96    Ok(public)
97}
98
99/// Recover public polynomial by interpolating coefficient-wise all
100/// polynomials.
101///
102/// It is assumed that the required number of commitments are provided.
103pub fn recover_public(
104    previous: &poly::Public,
105    commitments: BTreeMap<u32, poly::Public>,
106    threshold: u32,
107    concurrency: usize,
108) -> Result<poly::Public, Error> {
109    // Ensure we have enough commitments to interpolate
110    let required = previous.required();
111    if commitments.len() < required as usize {
112        return Err(Error::InsufficientDealings);
113    }
114
115    // Construct pool to perform interpolation
116    let pool = ThreadPoolBuilder::new()
117        .num_threads(concurrency)
118        .build()
119        .expect("unable to build thread pool");
120
121    // Perform interpolation over each coefficient
122    let new = match pool.install(|| {
123        (0..threshold)
124            .into_par_iter()
125            .map(|coeff| {
126                let evals: Vec<_> = commitments
127                    .iter()
128                    .map(|(dealer, commitment)| poly::Eval {
129                        index: *dealer,
130                        value: commitment.get(coeff),
131                    })
132                    .collect();
133                match poly::Public::recover(required, evals) {
134                    Ok(point) => Ok(point),
135                    Err(_) => Err(Error::PublicKeyInterpolationFailed),
136                }
137            })
138            .collect::<Result<Vec<_>, _>>()
139    }) {
140        Ok(points) => poly::Public::from(points),
141        Err(e) => return Err(e),
142    };
143
144    // Ensure public key matches
145    if previous.constant() != new.constant() {
146        return Err(Error::ReshareMismatch);
147    }
148    Ok(new)
149}