pvss 0.3.0

Publicly verifiable secret sharing implementations
Documentation
// implementation of SCRAPE: Scalable Randomness Attested by Public Entities
// https://eprint.iacr.org/2017/216.pdf

use super::crypto::*;
use super::dleq;
use super::math;
use super::pdleq;
use super::types::*;

pub type Secret<C> = Point<C>;

// a new escrowing context.
// this contains secret values (polynomial & secret) that are newly created.
// this also contains by-product (extra_generator & proof) which are useful for
// the protocol
#[derive(Clone)]
pub struct Escrow<C: EcOperation> {
    pub threshold: Threshold,
    pub extra_generator: Point<C>,
    pub polynomial: math::Polynomial<C>,
    pub secret: Secret<C>,
    pub proof: dleq::Proof<C>,
}

// Public values for a successful run of secret sharing.
//
// This contains everything for self verification and
// and the shares of each participants
//
// there should be N encrypted_shares and N commitments
// the parallel proofs should N elements too.
#[derive(Clone)]
pub struct PublicShares<C: EcOperation> {
    pub threshold: Threshold,
    pub extra_generator: Point<C>,
    pub secret_proof: dleq::Proof<C>,
    pub encrypted_shares: Vec<EncryptedShare<C>>,
    pub commitments: Vec<Commitment<C>>,
    pub proofs: pdleq::Proof<C>,
}

#[derive(Clone)]
pub struct Commitment<C: EcOperation> {
    point: Point<C>,
}

#[derive(Clone)]
pub struct EncryptedShare<C: EcOperation> {
    pub id: ShareId,
    encrypted_val: Point<C>,
}

#[derive(Clone)]
pub struct DecryptedShare<C: EcOperation> {
    pub id: ShareId,
    decrypted_val: Point<C>,
    proof: dleq::Proof<C>,
}

// create a new escrow parameter.
// the only parameter needed is the threshold necessary to be able to reconstruct.
pub fn escrow<C: EcOperation>(drg: &mut Drg, t: Threshold) -> Escrow<C> {
    assert!(t >= 1, "threshold is invalid; < 1");

    let poly = math::Polynomial::generate(drg, t - 1);
    let generator = Point::random_generator(drg);

    let secret = poly.at_zero();
    let g_s = Point::from_scalar(&secret);

    let challenge = Scalar::generate(drg);
    let dleq = dleq::DLEQ {
        g1: &Point::generator(),
        h1: &g_s,
        g2: &generator,
        h2: &generator.mul(&secret),
    };
    let proof = dleq::Proof::create(&challenge, &secret, &dleq);

    Escrow {
        threshold: t,
        extra_generator: generator,
        polynomial: poly,
        secret: g_s,
        proof,
    }
}

pub fn create_shares<C: EcOperation>(
    drg: &mut Drg,
    escrow: &Escrow<C>,
    pubs: &[PublicKey<C>],
) -> PublicShares<C> {
    let n = pubs.len();
    let mut shares = Vec::with_capacity(n);
    let mut commitments = Vec::with_capacity(n);
    let mut sis = Vec::with_capacity(n);
    let mut pparams = Vec::with_capacity(n);

    for (i, public) in ShareIdsSequence::new().zip(pubs.iter()) {
        let si = escrow.polynomial.evaluate(i.to_scalar());
        let esi = public.point.mul(&si);
        let vi = escrow.extra_generator.mul(&si);

        shares.push(EncryptedShare {
            id: i,
            encrypted_val: esi,
        });
        commitments.push(Commitment { point: vi });
        sis.push(si);
    }

    for (((s, c), public), si) in shares
        .iter()
        .zip(commitments.iter())
        .zip(pubs.iter())
        .zip(sis.iter())
    {
        {
            let w = Scalar::generate(drg);
            let dleq = dleq::DLEQ {
                g1: &escrow.extra_generator,
                h1: &c.point,
                g2: &public.point,
                h2: &s.encrypted_val,
            };
            pparams.push((w, si, dleq));
        }
    }

    // now create the parallel proof for all shares
    let pdleq = pdleq::Proof::create(pparams.as_slice());

    PublicShares {
        threshold: escrow.threshold,
        extra_generator: escrow.extra_generator.clone(),
        secret_proof: escrow.proof.clone(),
        encrypted_shares: shares,
        commitments,
        proofs: pdleq,
    }
}

impl<C: EcOperation> PublicShares<C> {
    pub fn number_participants(&self) -> u32 {
        self.commitments.len() as u32
    }

    pub fn verify(&self, drg: &mut Drg, publics: &[PublicKey<C>]) -> bool {
        // recreate all the DLEQs
        let mut dleqs = Vec::with_capacity(publics.len());
        for (i, public) in publics.iter().enumerate() {
            let vi = &self.commitments[i].point;
            let esi = &self.encrypted_shares[i].encrypted_val;
            let dleq = dleq::DLEQ {
                g1: &self.extra_generator,
                h1: &vi,
                g2: &public.point,
                h2: &esi,
            };
            dleqs.push(dleq);
        }
        // verify the parallel proof
        if !self.proofs.verify(dleqs.as_slice()) {
            return false;
        }

        // reed solomon check
        let n = self.number_participants();
        let poly = math::Polynomial::generate(drg, n - self.threshold - 1);

        let mut v = Point::infinity();
        for i in 0..n {
            let idx = i as usize;

            let mut cperp = poly.evaluate(Scalar::from_u32(i));
            for j in 0..n {
                if i != j {
                    cperp = cperp * (Scalar::from_u32(i) - Scalar::from_u32(j)).inverse();
                }
            }

            let commitment = &self.commitments[idx];
            v = v + commitment.point.mul(&cperp);
        }

        v == Point::infinity()
    }
}

impl<C: EcOperation> DecryptedShare<C> {
    pub fn verify(&self, public: &PublicKey<C>, eshare: &EncryptedShare<C>) -> bool {
        let dleq = dleq::DLEQ {
            g1: &Point::generator(),
            h1: &public.point,
            g2: &self.decrypted_val,
            h2: &eshare.encrypted_val,
        };
        self.proof.verify(&dleq)
    }
}

pub fn decrypt_share<C: EcOperation>(
    drg: &mut Drg,
    private: &PrivateKey<C>,
    public: &PublicKey<C>,
    share: &EncryptedShare<C>,
) -> DecryptedShare<C> {
    let challenge = Scalar::generate(drg);
    let xi = &private.scalar;
    let yi = &public.point;
    let lifted_yi = &share.encrypted_val;
    let si = lifted_yi.mul(&xi.inverse());
    let dleq = dleq::DLEQ {
        g1: &Point::generator(),
        h1: &yi,
        g2: &si,
        h2: &lifted_yi,
    };
    let proof = dleq::Proof::create(&challenge, &xi, &dleq);
    DecryptedShare {
        id: share.id,
        decrypted_val: si,
        proof,
    }
}

fn interpolate_one<C: EcOperation>(
    t: Threshold,
    sid: usize,
    shares: &[DecryptedShare<C>],
) -> Scalar<C> {
    let mut v = Scalar::multiplicative_identity();
    for j in 0..(t as usize) {
        if j != sid {
            let sj = shares[j].id.to_scalar();
            let si = shares[sid].id.to_scalar();
            let d = &sj - &si;
            v = v * sj * d.inverse();
        }
    }
    v
}

// Try to recover a secret
pub fn recover<C: EcOperation>(
    t: Threshold,
    shares: &[DecryptedShare<C>],
) -> Result<Secret<C>, ()> {
    if t as usize > shares.len() {
        return Err(());
    };
    let mut result = Point::infinity();
    for i in 0..(t as usize) {
        let v = interpolate_one(t, i, shares);
        result = result + shares[i].decrypted_val.mul(&v);
    }
    Ok(result)
}

pub fn verify_secret<C: EcOperation>(secret: Secret<C>, public_shares: &PublicShares<C>) -> bool {
    let mut commitment_interpolate = Point::infinity();
    for i in 0..(public_shares.threshold as usize) {
        let x = &public_shares.commitments[i].point;
        let li = {
            let mut v = Scalar::multiplicative_identity();
            for j in 0..(public_shares.threshold as usize) {
                if j != i {
                    let sj = Scalar::from_u32((j + 1) as u32);
                    let si = Scalar::from_u32((i + 1) as u32);
                    let d = &sj - &si;
                    v = v * sj * d.inverse();
                }
            }
            v
        };
        commitment_interpolate = commitment_interpolate + x.mul(&li);
    }
    let dleq = dleq::DLEQ {
        g1: &Point::generator(),
        h1: &secret,
        g2: &public_shares.extra_generator,
        h2: &commitment_interpolate,
    };
    public_shares.secret_proof.verify(&dleq)
}