#[cfg(feature = "std")]
use core::ops::Deref;
use std_shims::{vec, vec::Vec, collections::HashSet};
#[cfg(feature = "std")]
use std_shims::collections::HashMap;
#[cfg(feature = "std")]
use zeroize::Zeroizing;
#[cfg(feature = "std")]
use ciphersuite::group::ff::Field;
use ciphersuite::{
group::{Group, GroupEncoding},
Ciphersuite,
};
use crate::DkgError;
#[cfg(feature = "std")]
use crate::{Participant, ThresholdParams, ThresholdCore, lagrange};
fn check_keys<C: Ciphersuite>(keys: &[C::G]) -> Result<u16, DkgError<()>> {
if keys.is_empty() {
Err(DkgError::InvalidSigningSet)?;
}
let keys_len = u16::try_from(keys.len()).map_err(|_| DkgError::InvalidSigningSet)?;
if keys.iter().map(|key| key.to_bytes().as_ref().to_vec()).collect::<HashSet<_>>().len() !=
keys.len()
{
Err(DkgError::InvalidSigningSet)?;
}
Ok(keys_len)
}
fn binding_factor_transcript<C: Ciphersuite>(
context: &[u8],
keys: &[C::G],
) -> Result<Vec<u8>, DkgError<()>> {
let mut transcript = vec![];
transcript.push(u8::try_from(context.len()).map_err(|_| DkgError::InvalidSigningSet)?);
transcript.extend(context);
transcript.extend(u16::try_from(keys.len()).unwrap().to_le_bytes());
for key in keys {
transcript.extend(key.to_bytes().as_ref());
}
Ok(transcript)
}
fn binding_factor<C: Ciphersuite>(mut transcript: Vec<u8>, i: u16) -> C::F {
transcript.extend(i.to_le_bytes());
C::hash_to_F(b"musig", &transcript)
}
pub fn musig_key<C: Ciphersuite>(context: &[u8], keys: &[C::G]) -> Result<C::G, DkgError<()>> {
let keys_len = check_keys::<C>(keys)?;
let transcript = binding_factor_transcript::<C>(context, keys)?;
let mut res = C::G::identity();
for i in 1 ..= keys_len {
res += keys[usize::from(i - 1)] * binding_factor::<C>(transcript.clone(), i);
}
Ok(res)
}
#[cfg(feature = "std")]
pub fn musig<C: Ciphersuite>(
context: &[u8],
private_key: &Zeroizing<C::F>,
keys: &[C::G],
) -> Result<ThresholdCore<C>, DkgError<()>> {
let keys_len = check_keys::<C>(keys)?;
let our_pub_key = C::generator() * private_key.deref();
let Some(pos) = keys.iter().position(|key| *key == our_pub_key) else {
Err(DkgError::InvalidSigningSet)?
};
let params = ThresholdParams::new(
keys_len,
keys_len,
Participant::new((pos + 1).try_into().map_err(|_| DkgError::InvalidSigningSet)?)
.ok_or(DkgError::InvalidSigningSet)?,
)?;
let transcript = binding_factor_transcript::<C>(context, keys)?;
let mut binding = Vec::with_capacity(keys.len());
for i in 1 ..= keys_len {
binding.push(binding_factor::<C>(transcript.clone(), i));
}
let mut secret_share = private_key.clone();
*secret_share *= binding[pos];
let mut verification_shares = HashMap::new();
let included = (1 ..= keys_len)
.map(|l| Participant::new(l).ok_or(DkgError::InvalidSigningSet))
.collect::<Result<Vec<_>, _>>()?;
let mut group_key = C::G::identity();
for (l, p) in included.iter().enumerate() {
let bound = keys[l] * binding[l];
group_key += bound;
let lagrange_inv = lagrange::<C::F>(*p, &included).invert().unwrap();
if params.i() == *p {
*secret_share *= lagrange_inv;
}
verification_shares.insert(*p, bound * lagrange_inv);
}
debug_assert_eq!(C::generator() * secret_share.deref(), verification_shares[¶ms.i()]);
debug_assert_eq!(musig_key::<C>(context, keys).unwrap(), group_key);
Ok(ThresholdCore { params, secret_share, group_key, verification_shares })
}