1use std::{
6 hash::{Hash, Hasher},
7 str::FromStr,
8 sync::Arc,
9};
10
11pub use enum_dispatch::enum_dispatch;
12use fastcrypto::{
13 ed25519::Ed25519PublicKey,
14 encoding::{Base64, Encoding},
15 error::FastCryptoError,
16 hash::HashFunction,
17 secp256k1::Secp256k1PublicKey,
18 secp256r1::Secp256r1PublicKey,
19 traits::{EncodeDecodeBase64, ToFromBytes, VerifyingKey},
20};
21use once_cell::sync::OnceCell;
22use schemars::JsonSchema;
23use serde::{Deserialize, Serialize};
24use serde_with::serde_as;
25use shared_crypto::intent::IntentMessage;
26
27use crate::{
28 base_types::{EpochId, IotaAddress},
29 crypto::{CompressedSignature, DefaultHash, PublicKey, SignatureScheme},
30 digests::ZKLoginInputsDigest,
31 error::IotaError,
32 signature::{AuthenticatorTrait, GenericSignature, VerifyParams},
33 signature_verification::VerifiedDigestCache,
34 zk_login_authenticator::ZkLoginAuthenticator,
35};
36
37#[cfg(test)]
38#[path = "unit_tests/multisig_tests.rs"]
39mod multisig_tests;
40
41pub type WeightUnit = u8;
42pub type ThresholdUnit = u16;
43pub type BitmapUnit = u16;
44pub const MAX_SIGNER_IN_MULTISIG: usize = 10;
45pub const MAX_BITMAP_VALUE: BitmapUnit = 0b1111111111;
46#[serde_as]
49#[derive(Debug, Serialize, Deserialize, Clone, JsonSchema)]
50pub struct MultiSig {
51 sigs: Vec<CompressedSignature>,
53 bitmap: BitmapUnit,
56 multisig_pk: MultiSigPublicKey,
59 #[serde(skip)]
62 bytes: OnceCell<Vec<u8>>,
63}
64
65impl PartialEq for MultiSig {
67 fn eq(&self, other: &Self) -> bool {
68 self.sigs == other.sigs
69 && self.bitmap == other.bitmap
70 && self.multisig_pk == other.multisig_pk
71 }
72}
73
74impl Eq for MultiSig {}
76
77impl Hash for MultiSig {
79 fn hash<H: Hasher>(&self, state: &mut H) {
80 self.as_ref().hash(state);
81 }
82}
83
84impl AuthenticatorTrait for MultiSig {
85 fn verify_user_authenticator_epoch(
86 &self,
87 epoch_id: EpochId,
88 max_epoch_upper_bound_delta: Option<u64>,
89 ) -> Result<(), IotaError> {
90 self.get_zklogin_sigs()?.iter().try_for_each(|s| {
93 s.verify_user_authenticator_epoch(epoch_id, max_epoch_upper_bound_delta)
94 })
95 }
96
97 fn verify_claims<T>(
98 &self,
99 value: &IntentMessage<T>,
100 multisig_address: IotaAddress,
101 verify_params: &VerifyParams,
102 zklogin_inputs_cache: Arc<VerifiedDigestCache<ZKLoginInputsDigest>>,
103 ) -> Result<(), IotaError>
104 where
105 T: Serialize,
106 {
107 self.multisig_pk
108 .validate()
109 .map_err(|_| IotaError::InvalidSignature {
110 error: "Invalid multisig pubkey".to_string(),
111 })?;
112
113 if IotaAddress::from(&self.multisig_pk) != multisig_address {
114 return Err(IotaError::InvalidSignature {
115 error: "Invalid address derived from pks".to_string(),
116 });
117 }
118
119 if !self.get_zklogin_sigs()?.is_empty() && !verify_params.accept_zklogin_in_multisig {
120 return Err(IotaError::InvalidSignature {
121 error: "zkLogin sig not supported inside multisig".to_string(),
122 });
123 }
124
125 let mut weight_sum: u16 = 0;
126 let message = bcs::to_bytes(&value).expect("Message serialization should not fail");
127 let mut hasher = DefaultHash::default();
128 hasher.update(message);
129 let digest = hasher.finalize().digest;
130 for (sig, i) in self.sigs.iter().zip(as_indices(self.bitmap)?) {
134 let (subsig_pubkey, weight) =
135 self.multisig_pk
136 .pk_map
137 .get(i as usize)
138 .ok_or(IotaError::InvalidSignature {
139 error: "Invalid public keys index".to_string(),
140 })?;
141 let res = match sig {
142 CompressedSignature::Ed25519(s) => {
143 let pk =
144 Ed25519PublicKey::from_bytes(subsig_pubkey.as_ref()).map_err(|_| {
145 IotaError::InvalidSignature {
146 error: "Invalid ed25519 pk bytes".to_string(),
147 }
148 })?;
149 pk.verify(
150 &digest,
151 &s.try_into().map_err(|_| IotaError::InvalidSignature {
152 error: "Invalid ed25519 signature bytes".to_string(),
153 })?,
154 )
155 }
156 CompressedSignature::Secp256k1(s) => {
157 let pk =
158 Secp256k1PublicKey::from_bytes(subsig_pubkey.as_ref()).map_err(|_| {
159 IotaError::InvalidSignature {
160 error: "Invalid k1 pk bytes".to_string(),
161 }
162 })?;
163 pk.verify(
164 &digest,
165 &s.try_into().map_err(|_| IotaError::InvalidSignature {
166 error: "Invalid k1 signature bytes".to_string(),
167 })?,
168 )
169 }
170 CompressedSignature::Secp256r1(s) => {
171 let pk =
172 Secp256r1PublicKey::from_bytes(subsig_pubkey.as_ref()).map_err(|_| {
173 IotaError::InvalidSignature {
174 error: "Invalid r1 pk bytes".to_string(),
175 }
176 })?;
177 pk.verify(
178 &digest,
179 &s.try_into().map_err(|_| IotaError::InvalidSignature {
180 error: "Invalid r1 signature bytes".to_string(),
181 })?,
182 )
183 }
184 CompressedSignature::ZkLogin(z) => {
185 let authenticator = ZkLoginAuthenticator::from_bytes(&z.0).map_err(|_| {
186 IotaError::InvalidSignature {
187 error: "Invalid zklogin authenticator bytes".to_string(),
188 }
189 })?;
190 authenticator
191 .verify_claims(
192 value,
193 IotaAddress::from(subsig_pubkey),
194 verify_params,
195 zklogin_inputs_cache.clone(),
196 )
197 .map_err(|e| FastCryptoError::GeneralError(e.to_string()))
198 }
199 };
200 if res.is_ok() {
201 weight_sum += *weight as u16;
202 } else {
203 return res.map_err(|e| IotaError::InvalidSignature {
204 error: format!(
205 "Invalid sig for pk={} address={:?} error={:?}",
206 subsig_pubkey.encode_base64(),
207 IotaAddress::from(subsig_pubkey),
208 e.to_string()
209 ),
210 });
211 }
212 }
213 if weight_sum >= self.multisig_pk.threshold {
214 Ok(())
215 } else {
216 Err(IotaError::InvalidSignature {
217 error: format!(
218 "Insufficient weight={:?} threshold={:?}",
219 weight_sum, self.multisig_pk.threshold
220 ),
221 })
222 }
223 }
224}
225
226pub fn as_indices(bitmap: u16) -> Result<Vec<u8>, IotaError> {
229 if bitmap > MAX_BITMAP_VALUE {
230 return Err(IotaError::InvalidSignature {
231 error: "Invalid bitmap".to_string(),
232 });
233 }
234 let mut res = Vec::new();
235 for i in 0..10 {
236 if bitmap & (1 << i) != 0 {
237 res.push(i as u8);
238 }
239 }
240 Ok(res)
241}
242
243impl MultiSig {
244 pub fn insecure_new(
246 sigs: Vec<CompressedSignature>,
247 bitmap: BitmapUnit,
248 multisig_pk: MultiSigPublicKey,
249 ) -> Self {
250 Self {
251 sigs,
252 bitmap,
253 multisig_pk,
254 bytes: OnceCell::new(),
255 }
256 }
257 pub fn combine(
263 full_sigs: Vec<GenericSignature>,
264 multisig_pk: MultiSigPublicKey,
265 ) -> Result<Self, IotaError> {
266 multisig_pk
267 .validate()
268 .map_err(|_| IotaError::InvalidSignature {
269 error: "Invalid multisig public key".to_string(),
270 })?;
271
272 if full_sigs.len() > multisig_pk.pk_map.len() || full_sigs.is_empty() {
273 return Err(IotaError::InvalidSignature {
274 error: "Invalid number of signatures".to_string(),
275 });
276 }
277 let mut bitmap = 0;
278 let mut sigs = Vec::with_capacity(full_sigs.len());
279 for s in full_sigs {
280 let pk = s.to_public_key()?;
281 let index = multisig_pk
282 .get_index(&pk)
283 .ok_or(IotaError::IncorrectSigner {
284 error: format!("pk does not exist: {:?}", pk),
285 })?;
286 if bitmap & (1 << index) != 0 {
287 return Err(IotaError::InvalidSignature {
288 error: "Duplicate public key".to_string(),
289 });
290 }
291 bitmap |= 1 << index;
292 sigs.push(s.to_compressed()?);
293 }
294
295 Ok(MultiSig {
296 sigs,
297 bitmap,
298 multisig_pk,
299 bytes: OnceCell::new(),
300 })
301 }
302
303 pub fn init_and_validate(&mut self) -> Result<Self, FastCryptoError> {
304 if self.sigs.len() > self.multisig_pk.pk_map.len()
305 || self.sigs.is_empty()
306 || self.bitmap > MAX_BITMAP_VALUE
307 {
308 return Err(FastCryptoError::InvalidInput);
309 }
310 self.multisig_pk.validate()?;
311 Ok(self.to_owned())
312 }
313
314 pub fn get_pk(&self) -> &MultiSigPublicKey {
315 &self.multisig_pk
316 }
317
318 pub fn get_sigs(&self) -> &[CompressedSignature] {
319 &self.sigs
320 }
321
322 pub fn get_zklogin_sigs(&self) -> Result<Vec<ZkLoginAuthenticator>, IotaError> {
323 let authenticator_as_bytes: Vec<_> = self
324 .sigs
325 .iter()
326 .filter_map(|s| match s {
327 CompressedSignature::ZkLogin(z) => Some(z),
328 _ => None,
329 })
330 .collect();
331 authenticator_as_bytes
332 .iter()
333 .map(|z| {
334 ZkLoginAuthenticator::from_bytes(&z.0).map_err(|_| IotaError::InvalidSignature {
335 error: "Invalid zklogin authenticator bytes".to_string(),
336 })
337 })
338 .collect()
339 }
340
341 pub fn get_indices(&self) -> Result<Vec<u8>, IotaError> {
342 as_indices(self.bitmap)
343 }
344}
345
346impl ToFromBytes for MultiSig {
347 fn from_bytes(bytes: &[u8]) -> Result<MultiSig, FastCryptoError> {
348 if bytes.first().ok_or(FastCryptoError::InvalidInput)? != &SignatureScheme::MultiSig.flag()
350 {
351 return Err(FastCryptoError::InvalidInput);
352 }
353 let mut multisig: MultiSig =
354 bcs::from_bytes(&bytes[1..]).map_err(|_| FastCryptoError::InvalidSignature)?;
355 multisig.init_and_validate()
356 }
357}
358
359impl FromStr for MultiSig {
360 type Err = IotaError;
361
362 fn from_str(s: &str) -> Result<Self, Self::Err> {
363 let bytes = Base64::decode(s).map_err(|_| IotaError::InvalidSignature {
364 error: "Invalid base64 string".to_string(),
365 })?;
366 let sig = MultiSig::from_bytes(&bytes).map_err(|_| IotaError::InvalidSignature {
367 error: "Invalid multisig bytes".to_string(),
368 })?;
369 Ok(sig)
370 }
371}
372
373impl AsRef<[u8]> for MultiSig {
377 fn as_ref(&self) -> &[u8] {
378 self.bytes
379 .get_or_try_init::<_, eyre::Report>(|| {
380 let as_bytes = bcs::to_bytes(self).expect("BCS serialization should not fail");
381 let mut bytes = Vec::with_capacity(1 + as_bytes.len());
382 bytes.push(SignatureScheme::MultiSig.flag());
383 bytes.extend_from_slice(as_bytes.as_slice());
384 Ok(bytes)
385 })
386 .expect("OnceCell invariant violated")
387 }
388}
389
390#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
392pub struct MultiSigPublicKey {
393 pk_map: Vec<(PublicKey, WeightUnit)>,
395 threshold: ThresholdUnit,
398}
399
400impl MultiSigPublicKey {
401 pub fn insecure_new(pk_map: Vec<(PublicKey, WeightUnit)>, threshold: ThresholdUnit) -> Self {
403 Self { pk_map, threshold }
404 }
405
406 pub fn new(
407 pks: Vec<PublicKey>,
408 weights: Vec<WeightUnit>,
409 threshold: ThresholdUnit,
410 ) -> Result<Self, IotaError> {
411 if pks.is_empty()
412 || weights.is_empty()
413 || threshold == 0
414 || pks.len() != weights.len()
415 || pks.len() > MAX_SIGNER_IN_MULTISIG
416 || weights.contains(&0)
417 || weights
418 .iter()
419 .map(|w| *w as ThresholdUnit)
420 .sum::<ThresholdUnit>()
421 < threshold
422 || pks
423 .iter()
424 .enumerate()
425 .any(|(i, pk)| pks.iter().skip(i + 1).any(|other_pk| *pk == *other_pk))
426 {
427 return Err(IotaError::InvalidSignature {
428 error: "Invalid multisig public key construction".to_string(),
429 });
430 }
431
432 Ok(MultiSigPublicKey {
433 pk_map: pks.into_iter().zip(weights).collect(),
434 threshold,
435 })
436 }
437
438 pub fn get_index(&self, pk: &PublicKey) -> Option<u8> {
439 self.pk_map.iter().position(|x| &x.0 == pk).map(|x| x as u8)
440 }
441
442 pub fn threshold(&self) -> &ThresholdUnit {
443 &self.threshold
444 }
445
446 pub fn pubkeys(&self) -> &Vec<(PublicKey, WeightUnit)> {
447 &self.pk_map
448 }
449
450 pub fn validate(&self) -> Result<MultiSigPublicKey, FastCryptoError> {
451 let pk_map = self.pubkeys();
452 if self.threshold == 0
453 || pk_map.is_empty()
454 || pk_map.len() > MAX_SIGNER_IN_MULTISIG
455 || pk_map.iter().any(|(_pk, weight)| *weight == 0)
456 || pk_map
457 .iter()
458 .map(|(_pk, weight)| *weight as ThresholdUnit)
459 .sum::<ThresholdUnit>()
460 < self.threshold
461 || pk_map.iter().enumerate().any(|(i, (pk, _weight))| {
462 pk_map
463 .iter()
464 .skip(i + 1)
465 .any(|(other_pk, _weight)| *pk == *other_pk)
466 })
467 {
468 return Err(FastCryptoError::InvalidInput);
469 }
470 Ok(self.to_owned())
471 }
472}