use generic_ec_zkp::dlog_eq::non_interactive as dlog_eq;
pub fn ecdh<E: generic_ec::Curve>(
pubkey1: generic_ec::NonZero<generic_ec::Point<E>>,
privkey2: &generic_ec::NonZero<generic_ec::SecretScalar<E>>,
) -> generic_ec::NonZero<generic_ec::Point<E>> {
pubkey1 * privkey2
}
#[derive(Clone, Debug)]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(bound = "")
)]
pub struct PartialEvaluation<E: generic_ec::Curve> {
pub i: u16,
pub v: generic_ec::NonZero<generic_ec::Point<E>>,
pub pi: dlog_eq::Proof<E>,
}
pub fn partial_ecdh<E: generic_ec::Curve, D: digest::Digest>(
eid: &[u8],
i: u16,
counterparty_public_key: generic_ec::NonZero<generic_ec::Point<E>>,
secret_share: &generic_ec::NonZero<generic_ec::SecretScalar<E>>,
rng: &mut impl rand_core::CryptoRngCore,
) -> PartialEvaluation<E> {
let value = counterparty_public_key * secret_share;
let proof_data =
dlog_eq::Data::from_secret_key(secret_share, counterparty_public_key.into_inner());
let shared_state = SharedState {
eid,
prover_index: i,
};
let proof = dlog_eq::prove::<E, D>(rng, &shared_state, secret_share, proof_data);
PartialEvaluation {
i,
v: value,
pi: proof,
}
}
pub fn aggregate<E: generic_ec::Curve, D: digest::Digest>(
eid: &[u8],
counterparty_public_key: generic_ec::NonZero<generic_ec::Point<E>>,
partials: &[PartialEvaluation<E>],
public_shares: &[generic_ec::NonZero<generic_ec::Point<E>>],
share_preimages: Option<&[generic_ec::NonZero<generic_ec::Scalar<E>>]>,
) -> Result<generic_ec::Point<E>, crate::AggregateFailed> {
let mut blame = Vec::new();
for (partial, pub_share) in partials.iter().zip(public_shares) {
let shared_state = SharedState {
eid,
prover_index: partial.i,
};
let data = dlog_eq::Data {
gen1: generic_ec::Point::generator().into(),
prod1: pub_share.into_inner(),
gen2: counterparty_public_key.into_inner(),
prod2: partial.v.into_inner(),
};
if dlog_eq::verify::<E, D>(&shared_state, data, partial.pi).is_err() {
blame.push(partial.i);
};
}
if !blame.is_empty() {
return Err(crate::AggregateFailed::Verification(blame));
}
if let Some(share_preimages) = share_preimages {
let lagrange_coefficients = (0..(share_preimages.len()))
.map(|j| generic_ec_zkp::polynomial::lagrange_coefficient_at_zero(j, share_preimages))
.collect::<Option<Vec<_>>>()
.ok_or(crate::AggregateFailed::Lagrange)?;
Ok(generic_ec::Scalar::multiscalar_mul(
lagrange_coefficients
.into_iter()
.zip(partials.iter().map(|t| t.v)),
))
} else {
Ok(partials.iter().map(|t| t.v).sum())
}
}
#[derive(udigest::Digestable)]
struct SharedState<'a> {
eid: &'a [u8],
prover_index: u16,
}
#[cfg(test)]
mod test {
type E = generic_ec::curves::Secp256k1;
#[test_case::test_case(3, 5; "t3n5")]
#[test_case::test_case(5, 5; "t5n5")]
#[test_case::test_case(3, 7; "t3n7")]
fn aggregate_same_as_ecdh(t: u16, n: u16) {
let t_ = if t == n { None } else { Some(t) };
let t = usize::from(t);
let mut rng = rand_dev::DevRng::new();
let secret_key = generic_ec::NonZero::<generic_ec::SecretScalar<E>>::random(&mut rng);
let shares = key_share::trusted_dealer::builder::<E>(n)
.set_threshold(t_)
.set_shared_secret_key(secret_key.clone())
.generate_shares(&mut rng)
.unwrap();
let public_shares = &shares[0].public_shares;
let share_preimages = shares[0].vss_setup.as_ref().map(|vss| &vss.I[0..t]);
let data = generic_ec::Point::generator()
* generic_ec::NonZero::<generic_ec::Scalar<E>>::random(&mut rng);
let eid = b"test";
let ecdh = super::ecdh(data, &secret_key);
let partials = shares
.iter()
.zip(0..)
.map(|(s, i)| super::partial_ecdh::<E, sha2::Sha256>(eid, i, data, &s.x, &mut rng))
.collect::<Vec<_>>();
let restored = super::aggregate::<E, sha2::Sha256>(
eid,
data,
&partials[0..t],
public_shares,
share_preimages,
)
.unwrap();
assert_eq!(restored, ecdh);
}
}