1use crate::SignatureError;
2use crate::Verifier;
3use sui_sdk_types::MultisigAggregatedSignature;
4use sui_sdk_types::MultisigCommittee;
5use sui_sdk_types::MultisigMemberPublicKey;
6use sui_sdk_types::MultisigMemberSignature;
7use sui_sdk_types::UserSignature;
8
9#[derive(Default, Debug, Clone, PartialEq)]
10pub struct MultisigVerifier {
11 #[cfg(feature = "zklogin")]
12 zklogin_verifier: Option<crate::zklogin::ZkloginVerifier>,
13}
14
15impl MultisigVerifier {
16 pub fn new() -> Self {
17 Default::default()
18 }
19
20 fn verify_member_signature(
21 &self,
22 message: &[u8],
23 member_public_key: &MultisigMemberPublicKey,
24 signature: &MultisigMemberSignature,
25 ) -> Result<(), SignatureError> {
26 match (member_public_key, signature) {
27 #[cfg(not(feature = "ed25519"))]
28 (MultisigMemberPublicKey::Ed25519(_), MultisigMemberSignature::Ed25519(_)) => Err(
29 SignatureError::from_source("support for ed25519 is not enabled"),
30 ),
31 #[cfg(feature = "ed25519")]
32 (
33 MultisigMemberPublicKey::Ed25519(ed25519_public_key),
34 MultisigMemberSignature::Ed25519(ed25519_signature),
35 ) => crate::ed25519::Ed25519VerifyingKey::new(ed25519_public_key)?
36 .verify(message, ed25519_signature),
37
38 #[cfg(not(feature = "secp256k1"))]
39 (MultisigMemberPublicKey::Secp256k1(_), MultisigMemberSignature::Secp256k1(_)) => Err(
40 SignatureError::from_source("support for secp256k1 is not enabled"),
41 ),
42 #[cfg(feature = "secp256k1")]
43 (
44 MultisigMemberPublicKey::Secp256k1(k1_public_key),
45 MultisigMemberSignature::Secp256k1(k1_signature),
46 ) => crate::secp256k1::Secp256k1VerifyingKey::new(k1_public_key)?
47 .verify(message, k1_signature),
48
49 #[cfg(not(feature = "secp256r1"))]
50 (MultisigMemberPublicKey::Secp256r1(_), MultisigMemberSignature::Secp256r1(_)) => Err(
51 SignatureError::from_source("support for secp256r1 is not enabled"),
52 ),
53 #[cfg(feature = "secp256r1")]
54 (
55 MultisigMemberPublicKey::Secp256r1(r1_public_key),
56 MultisigMemberSignature::Secp256r1(r1_signature),
57 ) => crate::secp256r1::Secp256r1VerifyingKey::new(r1_public_key)?
58 .verify(message, r1_signature),
59
60 #[cfg(not(feature = "zklogin"))]
61 (MultisigMemberPublicKey::ZkLogin(_), MultisigMemberSignature::ZkLogin(_)) => Err(
62 SignatureError::from_source("support for zklogin is not enabled"),
63 ),
64 #[cfg(feature = "zklogin")]
65 (
66 MultisigMemberPublicKey::ZkLogin(zklogin_identifier),
67 MultisigMemberSignature::ZkLogin(zklogin_authenticator),
68 ) => {
69 let zklogin_verifier = self
70 .zklogin_verifier()
71 .ok_or_else(|| SignatureError::from_source("no zklogin verifier provided"))?;
72
73 if zklogin_identifier != zklogin_authenticator.inputs.public_identifier() {
75 return Err(SignatureError::from_source(
76 "member zklogin identifier does not match signature",
77 ));
78 }
79
80 zklogin_verifier.verify(message, zklogin_authenticator.as_ref())
81 }
82
83 #[cfg(not(feature = "passkey"))]
84 (MultisigMemberPublicKey::Passkey(_), MultisigMemberSignature::Passkey(_)) => Err(
85 SignatureError::from_source("support for passkey is not enabled"),
86 ),
87 #[cfg(feature = "passkey")]
88 (
89 MultisigMemberPublicKey::Passkey(passkey_public_key),
90 MultisigMemberSignature::Passkey(passkey_authenticator),
91 ) => {
92 if passkey_public_key != &passkey_authenticator.public_key() {
94 return Err(SignatureError::from_source(
95 "member passkey public_key does not match authenticator",
96 ));
97 }
98
99 crate::passkey::PasskeyVerifier::default().verify(message, passkey_authenticator)
100 }
101
102 #[cfg(not(feature = "mldsa65"))]
103 (MultisigMemberPublicKey::MlDsa65(_), MultisigMemberSignature::MlDsa65(_)) => Err(
104 SignatureError::from_source("support for mldsa65 is not enabled"),
105 ),
106 #[cfg(feature = "mldsa65")]
107 (
108 MultisigMemberPublicKey::MlDsa65(public_key),
109 MultisigMemberSignature::MlDsa65(signature),
110 ) => crate::mldsa65::MlDsa65VerifyingKey::new(public_key)?
111 .verify(message, signature.as_ref()),
112 _ => Err(SignatureError::from_source(
113 "member and signature scheme do not match",
114 )),
115 }
116 }
117}
118
119#[cfg(feature = "zklogin")]
120#[cfg_attr(doc_cfg, doc(cfg(feature = "zklogin")))]
121impl MultisigVerifier {
122 pub fn with_zklogin_verifier(&mut self, zklogin_verifier: crate::zklogin::ZkloginVerifier) {
123 self.zklogin_verifier = Some(zklogin_verifier);
124 }
125
126 pub fn zklogin_verifier(&self) -> Option<&crate::zklogin::ZkloginVerifier> {
127 self.zklogin_verifier.as_ref()
128 }
129
130 pub fn zklogin_verifier_mut(&mut self) -> Option<&mut crate::zklogin::ZkloginVerifier> {
131 self.zklogin_verifier.as_mut()
132 }
133}
134
135impl Verifier<MultisigAggregatedSignature> for MultisigVerifier {
136 fn verify(
137 &self,
138 message: &[u8],
139 signature: &MultisigAggregatedSignature,
140 ) -> Result<(), SignatureError> {
141 if !signature.committee().is_valid() {
142 return Err(SignatureError::from_source("invalid MultisigCommittee"));
143 }
144
145 if signature.signatures().len() != signature.bitmap().count_ones() as usize {
146 return Err(SignatureError::from_source(
147 "number of signatures does not match bitmap",
148 ));
149 }
150
151 if signature.signatures().len() > signature.committee().members().len() {
152 return Err(SignatureError::from_source(
153 "more signatures than committee members",
154 ));
155 }
156
157 let weight = BitmapIndices::new(signature.bitmap())
158 .map(|member_idx| {
159 signature
160 .committee()
161 .members()
162 .get(member_idx as usize)
163 .ok_or_else(|| SignatureError::from_source("invalid bitmap"))
164 })
165 .zip(signature.signatures())
166 .map(|(maybe_member, signature)| {
167 let member = maybe_member?;
168 self.verify_member_signature(message, member.public_key(), signature)
169 .map(|()| member.weight() as u16)
170 })
171 .sum::<Result<u16, SignatureError>>()?;
172
173 if weight >= signature.committee().threshold() {
174 Ok(())
175 } else {
176 Err(SignatureError::from_source(
177 "signature weight does not exceed threshold",
178 ))
179 }
180 }
181}
182
183impl Verifier<UserSignature> for MultisigVerifier {
184 fn verify(&self, message: &[u8], signature: &UserSignature) -> Result<(), SignatureError> {
185 let UserSignature::Multisig(signature) = signature else {
186 return Err(SignatureError::from_source("not a multisig signature"));
187 };
188
189 self.verify(message, signature)
190 }
191}
192
193struct BitmapIndices {
196 bitmap: u16,
197 range: std::ops::Range<u8>,
198}
199
200impl BitmapIndices {
201 pub fn new(bitmap: u16) -> Self {
202 Self {
203 bitmap,
204 range: 0..(u16::BITS as u8),
205 }
206 }
207}
208
209impl Iterator for BitmapIndices {
210 type Item = u8;
211
212 fn next(&mut self) -> Option<Self::Item> {
213 #[allow(clippy::while_let_on_iterator)]
214 while let Some(i) = self.range.next() {
215 if self.bitmap & (1 << i) != 0 {
216 return Some(i);
217 }
218 }
219
220 None
221 }
222}
223
224#[derive(Default, Debug, Clone, PartialEq)]
226pub struct UserSignatureVerifier {
227 inner: MultisigVerifier,
228}
229
230impl UserSignatureVerifier {
231 pub fn new() -> Self {
232 Default::default()
233 }
234}
235
236#[cfg(feature = "zklogin")]
237#[cfg_attr(doc_cfg, doc(cfg(feature = "zklogin")))]
238impl UserSignatureVerifier {
239 pub fn with_zklogin_verifier(&mut self, zklogin_verifier: crate::zklogin::ZkloginVerifier) {
240 self.inner.with_zklogin_verifier(zklogin_verifier);
241 }
242
243 pub fn zklogin_verifier(&self) -> Option<&crate::zklogin::ZkloginVerifier> {
244 self.inner.zklogin_verifier()
245 }
246
247 pub fn zklogin_verifier_mut(&mut self) -> Option<&mut crate::zklogin::ZkloginVerifier> {
248 self.inner.zklogin_verifier_mut()
249 }
250}
251
252impl Verifier<UserSignature> for UserSignatureVerifier {
253 fn verify(&self, message: &[u8], signature: &UserSignature) -> Result<(), SignatureError> {
254 match signature {
255 UserSignature::Simple(simple_signature) => {
256 crate::simple::SimpleVerifier.verify(message, simple_signature)
257 }
258 UserSignature::Multisig(multisig) => self.inner.verify(message, multisig),
259 #[cfg(not(feature = "zklogin"))]
260 UserSignature::ZkLogin(_) => Err(SignatureError::from_source(
261 "support for zklogin is not enabled",
262 )),
263 #[cfg(feature = "zklogin")]
264 UserSignature::ZkLogin(zklogin_authenticator) => {
265 let zklogin_verifier = self
266 .zklogin_verifier()
267 .ok_or_else(|| SignatureError::from_source("no zklogin verifier provided"))?;
268
269 zklogin_verifier.verify(message, zklogin_authenticator.as_ref())
270 }
271 #[cfg(not(feature = "passkey"))]
272 UserSignature::Passkey(_) => Err(SignatureError::from_source(
273 "support for passkey is not enabled",
274 )),
275 #[cfg(feature = "passkey")]
276 UserSignature::Passkey(passkey_authenticator) => {
277 crate::passkey::PasskeyVerifier::default().verify(message, passkey_authenticator)
278 }
279 _ => Err(SignatureError::from_source("unknown signature scheme")),
280 }
281 }
282}
283
284#[derive(Debug, Clone, PartialEq)]
285pub struct MultisigAggregator {
286 committee: MultisigCommittee,
287 signatures: std::collections::BTreeMap<usize, MultisigMemberSignature>,
288 signed_weight: u16,
289 message: Vec<u8>,
290 verifier: MultisigVerifier,
291}
292
293impl MultisigAggregator {
294 pub fn new_with_transaction(
295 committee: MultisigCommittee,
296 transaction: &sui_sdk_types::Transaction,
297 ) -> Self {
298 Self {
299 committee,
300 signatures: Default::default(),
301 signed_weight: 0,
302 message: transaction.signing_digest().to_vec(),
303 verifier: Default::default(),
304 }
305 }
306
307 pub fn new_with_message(
308 committee: MultisigCommittee,
309 message: &sui_sdk_types::PersonalMessage<'_>,
310 ) -> Self {
311 Self {
312 committee,
313 signatures: Default::default(),
314 signed_weight: 0,
315 message: message.signing_digest().to_vec(),
316 verifier: Default::default(),
317 }
318 }
319
320 pub fn verifier(&self) -> &MultisigVerifier {
321 &self.verifier
322 }
323
324 pub fn verifier_mut(&mut self) -> &mut MultisigVerifier {
325 &mut self.verifier
326 }
327
328 pub fn add_signature(&mut self, signature: UserSignature) -> Result<(), SignatureError> {
329 use std::collections::btree_map::Entry;
330
331 let (public_key, signature) = multisig_pubkey_and_signature_from_user_signature(signature)?;
332 let member_idx = self
333 .committee
334 .members()
335 .iter()
336 .position(|member| member.public_key() == &public_key)
337 .ok_or_else(|| {
338 SignatureError::from_source(
339 "provided signature does not belong to committee member",
340 )
341 })?;
342
343 self.verifier()
344 .verify_member_signature(&self.message, &public_key, &signature)?;
345
346 match self.signatures.entry(member_idx) {
347 Entry::Vacant(v) => {
348 v.insert(signature);
349 }
350 Entry::Occupied(_) => {
351 return Err(SignatureError::from_source(
352 "duplicate signature from same committee member",
353 ));
354 }
355 }
356
357 self.signed_weight += self.committee.members()[member_idx].weight() as u16;
358
359 Ok(())
360 }
361
362 pub fn finish(&self) -> Result<MultisigAggregatedSignature, SignatureError> {
363 if self.signed_weight < self.committee.threshold() {
364 return Err(SignatureError::from_source(
365 "insufficient signature weight to reach threshold",
366 ));
367 }
368
369 let (signatures, bitmap) = self.signatures.clone().into_iter().fold(
370 (Vec::new(), 0),
371 |(mut signatures, mut bitmap), (member_idx, signature)| {
372 bitmap |= 1 << member_idx;
373 signatures.push(signature);
374 (signatures, bitmap)
375 },
376 );
377
378 Ok(MultisigAggregatedSignature::new(
379 self.committee.clone(),
380 signatures,
381 bitmap,
382 ))
383 }
384}
385
386fn multisig_pubkey_and_signature_from_user_signature(
387 signature: UserSignature,
388) -> Result<(MultisigMemberPublicKey, MultisigMemberSignature), SignatureError> {
389 use sui_sdk_types::SimpleSignature;
390 match signature {
391 UserSignature::Simple(SimpleSignature::Ed25519 {
392 signature,
393 public_key,
394 }) => Ok((
395 MultisigMemberPublicKey::Ed25519(public_key),
396 MultisigMemberSignature::Ed25519(signature),
397 )),
398 UserSignature::Simple(SimpleSignature::Secp256k1 {
399 signature,
400 public_key,
401 }) => Ok((
402 MultisigMemberPublicKey::Secp256k1(public_key),
403 MultisigMemberSignature::Secp256k1(signature),
404 )),
405 UserSignature::Simple(SimpleSignature::Secp256r1 {
406 signature,
407 public_key,
408 }) => Ok((
409 MultisigMemberPublicKey::Secp256r1(public_key),
410 MultisigMemberSignature::Secp256r1(signature),
411 )),
412 UserSignature::Simple(SimpleSignature::MlDsa65 {
413 signature,
414 public_key,
415 }) => Ok((
416 MultisigMemberPublicKey::MlDsa65(public_key),
417 MultisigMemberSignature::MlDsa65(signature),
418 )),
419
420 #[cfg(not(feature = "zklogin"))]
421 UserSignature::ZkLogin(_) => Err(SignatureError::from_source(
422 "support for zklogin is not enabled",
423 )),
424 #[cfg(feature = "zklogin")]
425 UserSignature::ZkLogin(zklogin_authenticator) => {
426 let zklogin_identifier = zklogin_authenticator.inputs.public_identifier().to_owned();
427 Ok((
428 MultisigMemberPublicKey::ZkLogin(zklogin_identifier),
429 MultisigMemberSignature::ZkLogin(zklogin_authenticator),
430 ))
431 }
432
433 #[cfg(not(feature = "passkey"))]
434 UserSignature::Passkey(_) => Err(SignatureError::from_source(
435 "support for passkey is not enabled",
436 )),
437 #[cfg(feature = "passkey")]
438 UserSignature::Passkey(passkey_authenticator) => Ok((
439 MultisigMemberPublicKey::Passkey(passkey_authenticator.public_key()),
440 MultisigMemberSignature::Passkey(passkey_authenticator),
441 )),
442
443 UserSignature::Multisig(_) => Err(SignatureError::from_source("invalid siganture scheme")),
444
445 _ => Err(SignatureError::from_source("unknown siganture scheme")),
446 }
447}