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