#[cfg(feature = "std")]
use std::vec::Vec;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use curve25519_dalek::constants::RISTRETTO_BASEPOINT_TABLE;
use curve25519_dalek::ristretto::RistrettoPoint;
use curve25519_dalek::scalar::Scalar;
use curve25519_dalek::traits::Identity;
use rand::CryptoRng;
use rand::Rng;
use subtle::Choice;
use subtle::ConstantTimeEq;
use zeroize::Zeroize;
#[derive(Debug, Zeroize)]
#[zeroize(drop)]
pub(crate) struct NoncePair(pub(crate) Scalar, pub(crate) Scalar);
impl NoncePair {
pub fn new(mut csprng: impl CryptoRng + Rng) -> Self {
NoncePair(Scalar::random(&mut csprng), Scalar::random(&mut csprng))
}
}
impl From<NoncePair> for CommitmentShare {
fn from(other: NoncePair) -> CommitmentShare {
let x = &RISTRETTO_BASEPOINT_TABLE * &other.0;
let y = &RISTRETTO_BASEPOINT_TABLE * &other.1;
CommitmentShare {
hiding: Commitment {
nonce: other.0,
sealed: x,
},
binding: Commitment {
nonce: other.1,
sealed: y,
},
}
}
}
#[derive(Clone, Debug)]
pub(crate) struct Commitment {
pub(crate) nonce: Scalar,
pub(crate) sealed: RistrettoPoint,
}
impl Zeroize for Commitment {
fn zeroize(&mut self) {
self.nonce.zeroize();
self.sealed = RistrettoPoint::identity();
}
}
impl Drop for Commitment {
fn drop(&mut self) {
self.zeroize();
}
}
impl ConstantTimeEq for Commitment {
fn ct_eq(&self, other: &Commitment) -> Choice {
self.nonce.ct_eq(&other.nonce) &
self.sealed.compress().ct_eq(&other.sealed.compress())
}
}
#[derive(Clone, Debug, Zeroize)]
#[zeroize(drop)]
pub struct CommitmentShare {
pub(crate) hiding: Commitment,
pub(crate) binding: Commitment,
}
impl ConstantTimeEq for CommitmentShare {
fn ct_eq(&self, other: &CommitmentShare) -> Choice {
self.hiding.ct_eq(&other.hiding) & self.binding.ct_eq(&other.binding)
}
}
impl CommitmentShare {
pub fn publish(&self) -> (RistrettoPoint, RistrettoPoint) {
(self.hiding.sealed, self.binding.sealed)
}
}
#[derive(Debug)]
pub struct SecretCommitmentShareList {
pub commitments: Vec<CommitmentShare>,
}
#[derive(Debug)]
pub struct PublicCommitmentShareList {
pub participant_index: u32,
pub commitments: Vec<(RistrettoPoint, RistrettoPoint)>,
}
pub fn generate_commitment_share_lists(
mut csprng: impl CryptoRng + Rng,
participant_index: u32,
number_of_shares: usize,
) -> (PublicCommitmentShareList, SecretCommitmentShareList)
{
let mut commitments: Vec<CommitmentShare> = Vec::with_capacity(number_of_shares);
for _ in 0..number_of_shares {
commitments.push(CommitmentShare::from(NoncePair::new(&mut csprng)));
}
let mut published: Vec<(RistrettoPoint, RistrettoPoint)> = Vec::with_capacity(number_of_shares);
for commitment in commitments.iter() {
published.push(commitment.publish());
}
(PublicCommitmentShareList { participant_index, commitments: published },
SecretCommitmentShareList { commitments })
}
impl SecretCommitmentShareList {
pub fn drop_share(&mut self, share: CommitmentShare) {
let mut index = -1;
for (i, s) in self.commitments.iter().enumerate() {
if s.ct_eq(&share).into() {
index = i as isize;
}
}
if index >= 0 {
drop(self.commitments.remove(index as usize));
}
drop(share);
}
}
#[cfg(test)]
mod test {
use super::*;
use rand::rngs::OsRng;
#[test]
fn nonce_pair() {
let _nonce_pair = NoncePair::new(&mut OsRng);
}
#[test]
fn nonce_pair_into_commitment_share() {
let _commitment_share: CommitmentShare = NoncePair::new(&mut OsRng).into();
}
#[test]
fn commitment_share_list_generate() {
let (public_share_list, secret_share_list) = generate_commitment_share_lists(&mut OsRng, 0, 5);
assert_eq!(public_share_list.commitments[0].0.compress(),
(&secret_share_list.commitments[0].hiding.nonce * &RISTRETTO_BASEPOINT_TABLE).compress());
}
#[test]
fn drop_used_commitment_shares() {
let (_public_share_list, mut secret_share_list) = generate_commitment_share_lists(&mut OsRng, 3, 8);
assert!(secret_share_list.commitments.len() == 8);
let used_share = secret_share_list.commitments[0].clone();
secret_share_list.drop_share(used_share);
assert!(secret_share_list.commitments.len() == 7);
}
}