#[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(keys: &[C::G]) -> Result> { if keys.is_empty() { Err(DkgError::InvalidSigningSet)?; } // Too many signers let keys_len = u16::try_from(keys.len()).map_err(|_| DkgError::InvalidSigningSet)?; // Duplicated public keys if keys.iter().map(|key| key.to_bytes().as_ref().to_vec()).collect::>().len() != keys.len() { Err(DkgError::InvalidSigningSet)?; } Ok(keys_len) } // This function panics if called with keys whose length exceed 2**16. // This is fine since it's internal and all calls occur after calling check_keys, which does check // the keys' length. fn binding_factor_transcript( context: &[u8], keys: &[C::G], ) -> Result, 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(mut transcript: Vec, i: u16) -> C::F { transcript.extend(i.to_le_bytes()); C::hash_to_F(b"musig", &transcript) } /// The group key resulting from using this library's MuSig key gen. /// /// This function will return an error if the context is longer than 255 bytes. /// /// Creating an aggregate key with a list containing duplicated public keys will return an error. pub fn musig_key(context: &[u8], keys: &[C::G]) -> Result> { let keys_len = check_keys::(keys)?; let transcript = binding_factor_transcript::(context, keys)?; let mut res = C::G::identity(); for i in 1 ..= keys_len { res += keys[usize::from(i - 1)] * binding_factor::(transcript.clone(), i); } Ok(res) } /// A n-of-n non-interactive DKG which does not guarantee the usability of the resulting key. /// /// Creating an aggregate key with a list containing duplicated public keys returns an error. #[cfg(feature = "std")] pub fn musig( context: &[u8], private_key: &Zeroizing, keys: &[C::G], ) -> Result, DkgError<()>> { let keys_len = check_keys::(keys)?; let our_pub_key = C::generator() * private_key.deref(); let Some(pos) = keys.iter().position(|key| *key == our_pub_key) else { // Not present in signing set Err(DkgError::InvalidSigningSet)? }; let params = ThresholdParams::new( keys_len, keys_len, // These errors shouldn't be possible, as pos is bounded to len - 1 // Since len is prior guaranteed to be within u16::MAX, pos + 1 must also be Participant::new((pos + 1).try_into().map_err(|_| DkgError::InvalidSigningSet)?) .ok_or(DkgError::InvalidSigningSet)?, )?; // Calculate the binding factor per-key let transcript = binding_factor_transcript::(context, keys)?; let mut binding = Vec::with_capacity(keys.len()); for i in 1 ..= keys_len { binding.push(binding_factor::(transcript.clone(), i)); } // Multiply our private key by our binding factor let mut secret_share = private_key.clone(); *secret_share *= binding[pos]; // Calculate verification shares let mut verification_shares = HashMap::new(); // When this library offers a ThresholdView for a specific signing set, it applies the lagrange // factor // Since this is a n-of-n scheme, there's only one possible signing set, and one possible // lagrange factor // In the name of simplicity, we define the group key as the sum of all bound keys // Accordingly, the secret share must be multiplied by the inverse of the lagrange factor, along // with all verification shares // This is less performant than simply defining the group key as the sum of all post-lagrange // bound keys, yet the simplicity is preferred let included = (1 ..= keys_len) // This error also shouldn't be possible, for the same reasons as documented above .map(|l| Participant::new(l).ok_or(DkgError::InvalidSigningSet)) .collect::, _>>()?; 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::(*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::(context, keys).unwrap(), group_key); Ok(ThresholdCore { params, secret_share, group_key, verification_shares }) }