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