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