Add a context to MuSig key aggregation

This commit is contained in:
Luke Parker 2023-05-13 04:04:14 -04:00
parent 227176e4b8
commit 663b5f4b50
No known key found for this signature in database
3 changed files with 19 additions and 11 deletions

View file

@ -37,9 +37,13 @@ fn check_keys<C: Ciphersuite>(keys: &[C::G]) -> Result<u16, DkgError<()>> {
Ok(keys_len) Ok(keys_len)
} }
fn binding_factor_transcript<C: Ciphersuite>(keys: &[C::G]) -> RecommendedTranscript { fn binding_factor_transcript<C: Ciphersuite>(
context: &[u8],
keys: &[C::G],
) -> RecommendedTranscript {
let mut transcript = RecommendedTranscript::new(b"DKG MuSig v0.5"); let mut transcript = RecommendedTranscript::new(b"DKG MuSig v0.5");
transcript.domain_separate(b"musig_binding_factors"); transcript.append_message(b"context", context);
transcript.domain_separate(b"keys");
for key in keys { for key in keys {
transcript.append_message(b"key", key.to_bytes()); transcript.append_message(b"key", key.to_bytes());
} }
@ -47,6 +51,7 @@ fn binding_factor_transcript<C: Ciphersuite>(keys: &[C::G]) -> RecommendedTransc
} }
fn binding_factor<C: Ciphersuite>(mut transcript: RecommendedTranscript, i: u16) -> C::F { fn binding_factor<C: Ciphersuite>(mut transcript: RecommendedTranscript, i: u16) -> C::F {
transcript.domain_separate(b"participant");
transcript.append_message(b"participant", i.to_le_bytes()); transcript.append_message(b"participant", i.to_le_bytes());
C::hash_to_F(b"DKG-MuSig-binding_factor", &transcript.challenge(b"binding_factor")) C::hash_to_F(b"DKG-MuSig-binding_factor", &transcript.challenge(b"binding_factor"))
} }
@ -54,9 +59,9 @@ fn binding_factor<C: Ciphersuite>(mut transcript: RecommendedTranscript, i: u16)
/// The group key resulting from using this library's MuSig key gen. /// The group key resulting from using this library's MuSig key gen.
/// ///
/// Creating an aggregate key with a list containing duplicated public keys returns an error. /// Creating an aggregate key with a list containing duplicated public keys returns an error.
pub fn musig_key<C: Ciphersuite>(keys: &[C::G]) -> Result<C::G, DkgError<()>> { pub fn musig_key<C: Ciphersuite>(context: &[u8], keys: &[C::G]) -> Result<C::G, DkgError<()>> {
let keys_len = check_keys::<C>(keys)?; let keys_len = check_keys::<C>(keys)?;
let transcript = binding_factor_transcript::<C>(keys); let transcript = binding_factor_transcript::<C>(context, keys);
let mut res = C::G::identity(); let mut res = C::G::identity();
for i in 1 ..= keys_len { for i in 1 ..= keys_len {
res += keys[usize::from(i - 1)] * binding_factor::<C>(transcript.clone(), i); res += keys[usize::from(i - 1)] * binding_factor::<C>(transcript.clone(), i);
@ -69,6 +74,7 @@ pub fn musig_key<C: Ciphersuite>(keys: &[C::G]) -> Result<C::G, DkgError<()>> {
/// Creating an aggregate key with a list containing duplicated public keys returns an error. /// Creating an aggregate key with a list containing duplicated public keys returns an error.
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub fn musig<C: Ciphersuite>( pub fn musig<C: Ciphersuite>(
context: &[u8],
private_key: &Zeroizing<C::F>, private_key: &Zeroizing<C::F>,
keys: &[C::G], keys: &[C::G],
) -> Result<ThresholdCore<C>, DkgError<()>> { ) -> Result<ThresholdCore<C>, DkgError<()>> {
@ -89,7 +95,7 @@ pub fn musig<C: Ciphersuite>(
)?; )?;
// Calculate the binding factor per-key // Calculate the binding factor per-key
let transcript = binding_factor_transcript::<C>(keys); let transcript = binding_factor_transcript::<C>(context, keys);
let mut binding = Vec::with_capacity(keys.len()); let mut binding = Vec::with_capacity(keys.len());
for i in 1 ..= keys_len { for i in 1 ..= keys_len {
binding.push(binding_factor::<C>(transcript.clone(), i)); binding.push(binding_factor::<C>(transcript.clone(), i));
@ -126,7 +132,7 @@ pub fn musig<C: Ciphersuite>(
verification_shares.insert(*p, bound * lagrange_inv); verification_shares.insert(*p, bound * lagrange_inv);
} }
debug_assert_eq!(C::generator() * secret_share.deref(), verification_shares[&params.i()]); debug_assert_eq!(C::generator() * secret_share.deref(), verification_shares[&params.i()]);
debug_assert_eq!(musig_key::<C>(keys).unwrap(), group_key); debug_assert_eq!(musig_key::<C>(context, keys).unwrap(), group_key);
Ok(ThresholdCore { params, secret_share, group_key, verification_shares }) Ok(ThresholdCore { params, secret_share, group_key, verification_shares })
} }

View file

@ -81,7 +81,7 @@ pub fn musig_key_gen<R: RngCore + CryptoRng, C: Ciphersuite>(
let mut res = HashMap::new(); let mut res = HashMap::new();
for key in keys { for key in keys {
let these_keys = musig_fn::<C>(&key, &pub_keys).unwrap(); let these_keys = musig_fn::<C>(b"Test MuSig Key Gen", &key, &pub_keys).unwrap();
res.insert(these_keys.params().i(), ThresholdKeys::new(these_keys)); res.insert(these_keys.params().i(), ThresholdKeys::new(these_keys));
} }

View file

@ -21,18 +21,20 @@ pub fn test_musig<R: RngCore + CryptoRng, C: Ciphersuite>(rng: &mut R) {
keys.push(key); keys.push(key);
} }
const CONTEXT: &[u8] = b"MuSig Test";
// Empty signing set // Empty signing set
assert!(musig::<C>(&Zeroizing::new(C::F::ZERO), &[]).is_err()); assert!(musig::<C>(CONTEXT, &Zeroizing::new(C::F::ZERO), &[]).is_err());
// Signing set we're not part of // Signing set we're not part of
assert!(musig::<C>(&Zeroizing::new(C::F::ZERO), &[C::generator()]).is_err()); assert!(musig::<C>(CONTEXT, &Zeroizing::new(C::F::ZERO), &[C::generator()]).is_err());
// Test with n keys // Test with n keys
{ {
let mut created_keys = HashMap::new(); let mut created_keys = HashMap::new();
let mut verification_shares = HashMap::new(); let mut verification_shares = HashMap::new();
let group_key = musig_key::<C>(&pub_keys).unwrap(); let group_key = musig_key::<C>(CONTEXT, &pub_keys).unwrap();
for (i, key) in keys.iter().enumerate() { for (i, key) in keys.iter().enumerate() {
let these_keys = musig::<C>(key, &pub_keys).unwrap(); let these_keys = musig::<C>(CONTEXT, key, &pub_keys).unwrap();
assert_eq!(these_keys.params().t(), PARTICIPANTS); assert_eq!(these_keys.params().t(), PARTICIPANTS);
assert_eq!(these_keys.params().n(), PARTICIPANTS); assert_eq!(these_keys.params().n(), PARTICIPANTS);
assert_eq!(usize::from(these_keys.params().i().0), i + 1); assert_eq!(usize::from(these_keys.params().i().0), i + 1);