1#![cfg_attr(docsrs, feature(doc_auto_cfg))]
2#![doc = include_str!("../README.md")]
3#![cfg_attr(not(feature = "std"), no_std)]
4
5use core::ops::Deref;
6use std_shims::{
7 vec,
8 vec::Vec,
9 collections::{HashSet, HashMap},
10};
11
12use zeroize::Zeroizing;
13
14use ciphersuite::{group::GroupEncoding, Ciphersuite};
15
16pub use dkg::*;
17
18#[cfg(test)]
19mod tests;
20
21#[derive(Clone, PartialEq, Eq, Debug, thiserror::Error)]
23pub enum MusigError<C: Ciphersuite> {
24 #[error("no keys provided")]
26 NoKeysProvided,
27 #[error("too many keys (allowed {max}, provided {provided})")]
29 TooManyKeysProvided {
30 max: u16,
32 provided: usize,
34 },
35 #[error("a participant was duplicated")]
37 DuplicatedParticipant(C::G),
38 #[error("private key's public key wasn't present in the list of public keys")]
40 NotPresent,
41 #[error("error from dkg ({0})")]
43 DkgError(DkgError),
44}
45
46fn check_keys<C: Ciphersuite>(keys: &[C::G]) -> Result<u16, MusigError<C>> {
47 if keys.is_empty() {
48 Err(MusigError::NoKeysProvided)?;
49 }
50
51 let keys_len = u16::try_from(keys.len())
52 .map_err(|_| MusigError::TooManyKeysProvided { max: u16::MAX, provided: keys.len() })?;
53
54 let mut set = HashSet::with_capacity(keys.len());
55 for key in keys {
56 let bytes = key.to_bytes().as_ref().to_vec();
57 if !set.insert(bytes) {
58 Err(MusigError::DuplicatedParticipant(*key))?;
59 }
60 }
61
62 Ok(keys_len)
63}
64
65fn binding_factor_transcript<C: Ciphersuite>(
66 context: [u8; 32],
67 keys_len: u16,
68 keys: &[C::G],
69) -> Vec<u8> {
70 debug_assert_eq!(usize::from(keys_len), keys.len());
71
72 let mut transcript = vec![];
73 transcript.extend(&context);
74 transcript.extend(keys_len.to_le_bytes());
75 for key in keys {
76 transcript.extend(key.to_bytes().as_ref());
77 }
78 transcript
79}
80
81fn binding_factor<C: Ciphersuite>(mut transcript: Vec<u8>, i: u16) -> C::F {
82 transcript.extend(i.to_le_bytes());
83 C::hash_to_F(b"dkg-musig", &transcript)
84}
85
86#[allow(clippy::type_complexity)]
87fn musig_key_multiexp<C: Ciphersuite>(
88 context: [u8; 32],
89 keys: &[C::G],
90) -> Result<Vec<(C::F, C::G)>, MusigError<C>> {
91 let keys_len = check_keys::<C>(keys)?;
92 let transcript = binding_factor_transcript::<C>(context, keys_len, keys);
93 let mut multiexp = Vec::with_capacity(keys.len());
94 for i in 1 ..= keys_len {
95 multiexp.push((binding_factor::<C>(transcript.clone(), i), keys[usize::from(i - 1)]));
96 }
97 Ok(multiexp)
98}
99
100pub fn musig_key_vartime<C: Ciphersuite>(
104 context: [u8; 32],
105 keys: &[C::G],
106) -> Result<C::G, MusigError<C>> {
107 Ok(multiexp::multiexp_vartime(&musig_key_multiexp(context, keys)?))
108}
109
110pub fn musig_key<C: Ciphersuite>(context: [u8; 32], keys: &[C::G]) -> Result<C::G, MusigError<C>> {
112 Ok(multiexp::multiexp(&musig_key_multiexp(context, keys)?))
113}
114
115pub fn musig<C: Ciphersuite>(
117 context: [u8; 32],
118 private_key: Zeroizing<C::F>,
119 keys: &[C::G],
120) -> Result<ThresholdKeys<C>, MusigError<C>> {
121 let our_pub_key = C::generator() * private_key.deref();
122 let Some(our_i) = keys.iter().position(|key| *key == our_pub_key) else {
123 Err(MusigError::DkgError(DkgError::NotParticipating))?
124 };
125
126 let keys_len: u16 = check_keys::<C>(keys)?;
127
128 let params = ThresholdParams::new(
129 keys_len,
130 keys_len,
131 Participant::new(
133 u16::try_from(our_i).expect("keys.len() <= u16::MAX yet index of keys > u16::MAX?") + 1,
134 )
135 .expect("i + 1 != 0"),
136 )
137 .map_err(MusigError::DkgError)?;
138
139 let transcript = binding_factor_transcript::<C>(context, keys_len, keys);
140 let mut binding_factors = Vec::with_capacity(keys.len());
141 let mut multiexp = Vec::with_capacity(keys.len());
142 let mut verification_shares = HashMap::with_capacity(keys.len());
143 for (i, key) in (1 ..= keys_len).zip(keys.iter().copied()) {
144 let binding_factor = binding_factor::<C>(transcript.clone(), i);
145 binding_factors.push(binding_factor);
146 multiexp.push((binding_factor, key));
147
148 let i = Participant::new(i).expect("non-zero u16 wasn't a valid Participant index?");
149 verification_shares.insert(i, key);
150 }
151 let group_key = multiexp::multiexp(&multiexp);
152 debug_assert_eq!(our_pub_key, verification_shares[¶ms.i()]);
153 debug_assert_eq!(musig_key_vartime::<C>(context, keys).unwrap(), group_key);
154
155 ThresholdKeys::new(
156 params,
157 Interpolation::Constant(binding_factors),
158 private_key,
159 verification_shares,
160 )
161 .map_err(MusigError::DkgError)
162}