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)]
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)]
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
279pub struct MultisigAggregator {
280 committee: MultisigCommittee,
281 signatures: std::collections::BTreeMap<usize, MultisigMemberSignature>,
282 signed_weight: u16,
283 message: Vec<u8>,
284 verifier: MultisigVerifier,
285}
286
287impl MultisigAggregator {
288 pub fn new_with_transaction(
289 committee: MultisigCommittee,
290 transaction: &sui_sdk_types::Transaction,
291 ) -> Self {
292 Self {
293 committee,
294 signatures: Default::default(),
295 signed_weight: 0,
296 message: transaction.signing_digest().to_vec(),
297 verifier: Default::default(),
298 }
299 }
300
301 pub fn new_with_message(
302 committee: MultisigCommittee,
303 message: &sui_sdk_types::PersonalMessage<'_>,
304 ) -> Self {
305 Self {
306 committee,
307 signatures: Default::default(),
308 signed_weight: 0,
309 message: message.signing_digest().to_vec(),
310 verifier: Default::default(),
311 }
312 }
313
314 pub fn verifier(&self) -> &MultisigVerifier {
315 &self.verifier
316 }
317
318 pub fn verifier_mut(&mut self) -> &mut MultisigVerifier {
319 &mut self.verifier
320 }
321
322 pub fn add_signature(&mut self, signature: UserSignature) -> Result<(), SignatureError> {
323 use std::collections::btree_map::Entry;
324
325 let (public_key, signature) = multisig_pubkey_and_signature_from_user_signature(signature)?;
326 let member_idx = self
327 .committee
328 .members()
329 .iter()
330 .position(|member| member.public_key() == &public_key)
331 .ok_or_else(|| {
332 SignatureError::from_source(
333 "provided signature does not belong to committee member",
334 )
335 })?;
336
337 self.verifier()
338 .verify_member_signature(&self.message, &public_key, &signature)?;
339
340 match self.signatures.entry(member_idx) {
341 Entry::Vacant(v) => {
342 v.insert(signature);
343 }
344 Entry::Occupied(_) => {
345 return Err(SignatureError::from_source(
346 "duplicate signature from same committee member",
347 ))
348 }
349 }
350
351 self.signed_weight += self.committee.members()[member_idx].weight() as u16;
352
353 Ok(())
354 }
355
356 pub fn finish(&mut self) -> Result<MultisigAggregatedSignature, SignatureError> {
357 if self.signed_weight < self.committee.threshold() {
358 return Err(SignatureError::from_source(
359 "insufficient signature weight to reach threshold",
360 ));
361 }
362
363 let (signatures, bitmap) = self.signatures.clone().into_iter().fold(
364 (Vec::new(), 0),
365 |(mut signatures, mut bitmap), (member_idx, signature)| {
366 bitmap |= 1 << member_idx;
367 signatures.push(signature);
368 (signatures, bitmap)
369 },
370 );
371
372 Ok(MultisigAggregatedSignature::new(
373 self.committee.clone(),
374 signatures,
375 bitmap,
376 ))
377 }
378}
379
380fn multisig_pubkey_and_signature_from_user_signature(
381 signature: UserSignature,
382) -> Result<(MultisigMemberPublicKey, MultisigMemberSignature), SignatureError> {
383 use sui_sdk_types::SimpleSignature;
384 match signature {
385 UserSignature::Simple(SimpleSignature::Ed25519 {
386 signature,
387 public_key,
388 }) => Ok((
389 MultisigMemberPublicKey::Ed25519(public_key),
390 MultisigMemberSignature::Ed25519(signature),
391 )),
392 UserSignature::Simple(SimpleSignature::Secp256k1 {
393 signature,
394 public_key,
395 }) => Ok((
396 MultisigMemberPublicKey::Secp256k1(public_key),
397 MultisigMemberSignature::Secp256k1(signature),
398 )),
399 UserSignature::Simple(SimpleSignature::Secp256r1 {
400 signature,
401 public_key,
402 }) => Ok((
403 MultisigMemberPublicKey::Secp256r1(public_key),
404 MultisigMemberSignature::Secp256r1(signature),
405 )),
406
407 #[cfg(not(feature = "zklogin"))]
408 UserSignature::ZkLogin(_) => Err(SignatureError::from_source(
409 "support for zklogin is not enabled",
410 )),
411 #[cfg(feature = "zklogin")]
412 UserSignature::ZkLogin(zklogin_authenticator) => {
413 let zklogin_identifier = zklogin_authenticator
414 .inputs
415 .public_identifier()
416 .map_err(SignatureError::from_source)?;
417 Ok((
418 MultisigMemberPublicKey::ZkLogin(zklogin_identifier),
419 MultisigMemberSignature::ZkLogin(zklogin_authenticator),
420 ))
421 }
422
423 #[cfg(not(feature = "passkey"))]
424 UserSignature::Passkey(_) => Err(SignatureError::from_source(
425 "support for passkey is not enabled",
426 )),
427 #[cfg(feature = "passkey")]
428 UserSignature::Passkey(passkey_authenticator) => Ok((
429 MultisigMemberPublicKey::Passkey(passkey_authenticator.public_key()),
430 MultisigMemberSignature::Passkey(passkey_authenticator),
431 )),
432
433 UserSignature::Multisig(_) => Err(SignatureError::from_source("invalid siganture scheme")),
434 }
435}