commonware_cryptography/bls12381/dkg/
ops.rs

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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
//! Stateless operations useful in a DKG/Resharing procedure.

use crate::bls12381::{
    dkg::Error,
    primitives::{group::Share, poly},
};
use rayon::{prelude::*, ThreadPoolBuilder};
use std::collections::BTreeMap;

/// Generate shares and a commitment.
///
/// # Arguments
/// * `share` - The dealer's share of the previous DKG (if any)
/// * `n` - The total number of participants in the round
/// * `t` - The threshold number of participants required to reconstruct the secret
pub fn generate_shares(share: Option<Share>, n: u32, t: u32) -> (poly::Public, Vec<Share>) {
    // Generate a secret polynomial and commit to it
    let mut secret = poly::new(t - 1);
    if let Some(share) = share {
        // Set the free coefficient of the secret polynomial to the secret
        // of the previous DKG
        secret.set(0, share.private);
    }

    // Commit to polynomial and generate shares
    let commitment = poly::Public::commit(secret.clone());
    let shares = (0..n)
        .map(|i| {
            let eval = secret.evaluate(i);
            Share {
                index: eval.index,
                private: eval.value,
            }
        })
        .collect::<Vec<_>>();
    (commitment, shares)
}

/// Verify that a given commitment is valid for a dealer. If a previous
/// polynomial is provided, verify that the commitment is on that polynomial.
pub fn verify_commitment(
    previous: Option<&poly::Public>,
    dealer: u32,
    commitment: &poly::Public,
    t: u32,
) -> Result<(), Error> {
    if let Some(previous) = previous {
        let expected = previous.evaluate(dealer).value;
        if expected != *commitment.constant() {
            return Err(Error::UnexpectedPolynomial);
        }
    }
    if commitment.degree() != t - 1 {
        return Err(Error::CommitmentWrongDegree);
    }
    Ok(())
}

/// Verify that a given share is valid for a specified recipient.
pub fn verify_share(
    previous: Option<&poly::Public>,
    dealer: u32,
    commitment: &poly::Public,
    t: u32,
    recipient: u32,
    share: &Share,
) -> Result<(), Error> {
    // Verify that commitment is on previous public polynomial (if provided)
    verify_commitment(previous, dealer, commitment, t)?;

    // Check if share is valid
    if share.index != recipient {
        return Err(Error::MisdirectedShare);
    }
    let expected = share.public();
    let given = commitment.evaluate(share.index);
    if given.value != expected {
        return Err(Error::ShareWrongCommitment);
    }
    Ok(())
}

/// Construct a new public polynomial by summing all commitments.
pub fn construct_public(
    commitments: Vec<poly::Public>,
    required: u32,
) -> Result<poly::Public, Error> {
    if commitments.len() < required as usize {
        return Err(Error::InsufficientDealings);
    }
    let mut public = poly::Public::zero();
    for commitment in commitments {
        public.add(&commitment);
    }
    Ok(public)
}

/// Recover public polynomial by interpolating coeffcient-wise all
/// polynomials.
///
/// It is assumed that the required number of commitments are provided.
pub fn recover_public(
    previous: &poly::Public,
    commitments: BTreeMap<u32, poly::Public>,
    threshold: u32,
    concurrency: usize,
) -> Result<poly::Public, Error> {
    // Ensure we have enough commitments to interpolate
    let required = previous.required();
    if commitments.len() < required as usize {
        return Err(Error::InsufficientDealings);
    }

    // Construct pool to perform interpolation
    let pool = ThreadPoolBuilder::new()
        .num_threads(concurrency)
        .build()
        .expect("unable to build thread pool");

    // Perform interpolation over each coefficient
    let new = match pool.install(|| {
        (0..threshold)
            .into_par_iter()
            .map(|coeff| {
                let evals: Vec<_> = commitments
                    .iter()
                    .map(|(dealer, commitment)| poly::Eval {
                        index: *dealer,
                        value: commitment.get(coeff),
                    })
                    .collect();
                match poly::Public::recover(required, evals) {
                    Ok(point) => Ok(point),
                    Err(_) => Err(Error::PublicKeyInterpolationFailed),
                }
            })
            .collect::<Result<Vec<_>, _>>()
    }) {
        Ok(points) => poly::Public::from(points),
        Err(e) => return Err(e),
    };

    // Ensure public key matches
    if previous.constant() != new.constant() {
        return Err(Error::ReshareMismatch);
    }
    Ok(new)
}