1use std::{hash::Hash, sync::Arc};
6
7pub use enum_dispatch::enum_dispatch;
8use fastcrypto::{
9 ed25519::{Ed25519PublicKey, Ed25519Signature},
10 error::FastCryptoError,
11 secp256k1::{Secp256k1PublicKey, Secp256k1Signature},
12 secp256r1::{Secp256r1PublicKey, Secp256r1Signature},
13 traits::{EncodeDecodeBase64, ToFromBytes},
14};
15use fastcrypto_zkp::bn254::{
16 zk_login::{JWK, JwkId},
17 zk_login_api::ZkLoginEnv,
18};
19use im::hashmap::HashMap as ImHashMap;
20use iota_sdk_types::crypto::IntentMessage;
21use schemars::JsonSchema;
22use serde::Serialize;
23
24use crate::{
25 base_types::IotaAddress,
26 committee::EpochId,
27 crypto::{
28 CompressedSignature, IotaSignature, PasskeyAuthenticatorAsBytes, PublicKey, Signature,
29 SignatureScheme, ZkLoginAuthenticatorAsBytes,
30 },
31 digests::ZKLoginInputsDigest,
32 error::{IotaError, IotaResult},
33 move_authenticator::MoveAuthenticator,
34 multisig::MultiSig,
35 passkey_authenticator::PasskeyAuthenticator,
36 signature_verification::VerifiedDigestCache,
37 zk_login_authenticator::ZkLoginAuthenticator,
38};
39#[derive(Default, Debug, Clone)]
40pub struct VerifyParams {
41 pub oidc_provider_jwks: ImHashMap<JwkId, JWK>,
43 pub zk_login_env: ZkLoginEnv,
44 pub accept_zklogin_in_multisig: bool,
45 pub accept_passkey_in_multisig: bool,
46 pub zklogin_max_epoch_upper_bound_delta: Option<u64>,
47 pub additional_multisig_checks: bool,
48}
49
50impl VerifyParams {
51 pub fn new(
52 oidc_provider_jwks: ImHashMap<JwkId, JWK>,
53 zk_login_env: ZkLoginEnv,
54 accept_zklogin_in_multisig: bool,
55 accept_passkey_in_multisig: bool,
56 zklogin_max_epoch_upper_bound_delta: Option<u64>,
57 additional_multisig_checks: bool,
58 ) -> Self {
59 Self {
60 oidc_provider_jwks,
61 zk_login_env,
62 accept_zklogin_in_multisig,
63 accept_passkey_in_multisig,
64 zklogin_max_epoch_upper_bound_delta,
65 additional_multisig_checks,
66 }
67 }
68}
69
70#[enum_dispatch]
72pub trait AuthenticatorTrait {
73 fn verify_user_authenticator_epoch(
74 &self,
75 epoch: EpochId,
76 max_epoch_upper_bound_delta: Option<u64>,
77 ) -> IotaResult;
78
79 fn verify_claims<T>(
80 &self,
81 value: &IntentMessage<T>,
82 author: IotaAddress,
83 aux_verify_data: &VerifyParams,
84 zklogin_inputs_cache: Arc<VerifiedDigestCache<ZKLoginInputsDigest>>,
85 ) -> IotaResult
86 where
87 T: Serialize;
88}
89
90#[enum_dispatch(AuthenticatorTrait)]
96#[derive(Debug, Clone, PartialEq, Eq, JsonSchema, Hash)]
97pub enum GenericSignature {
98 MultiSig,
99 Signature,
100 ZkLoginAuthenticator,
101 PasskeyAuthenticator,
102 MoveAuthenticator,
103}
104
105impl GenericSignature {
106 pub fn is_zklogin(&self) -> bool {
107 matches!(self, GenericSignature::ZkLoginAuthenticator(_))
108 }
109 pub fn is_passkey(&self) -> bool {
110 matches!(self, GenericSignature::PasskeyAuthenticator(_))
111 }
112
113 pub fn is_upgraded_multisig(&self) -> bool {
114 matches!(self, GenericSignature::MultiSig(_))
115 }
116
117 pub fn is_move_authenticator(&self) -> bool {
118 matches!(self, GenericSignature::MoveAuthenticator(_))
119 }
120
121 pub fn verify_authenticator<T>(
122 &self,
123 value: &IntentMessage<T>,
124 author: IotaAddress,
125 epoch: EpochId,
126 verify_params: &VerifyParams,
127 zklogin_inputs_cache: Arc<VerifiedDigestCache<ZKLoginInputsDigest>>,
128 ) -> IotaResult
129 where
130 T: Serialize,
131 {
132 self.verify_user_authenticator_epoch(
133 epoch,
134 verify_params.zklogin_max_epoch_upper_bound_delta,
135 )?;
136 self.verify_claims(value, author, verify_params, zklogin_inputs_cache)
137 }
138
139 pub fn to_compressed(&self) -> Result<CompressedSignature, IotaError> {
143 match self {
144 GenericSignature::Signature(s) => {
145 let bytes = s.signature_bytes();
146 match s.scheme() {
147 SignatureScheme::ED25519 => Ok(CompressedSignature::Ed25519(
148 (&Ed25519Signature::from_bytes(bytes).map_err(|_| {
149 IotaError::InvalidSignature {
150 error: "Cannot parse ed25519 sig".to_string(),
151 }
152 })?)
153 .into(),
154 )),
155 SignatureScheme::Secp256k1 => Ok(CompressedSignature::Secp256k1(
156 (&Secp256k1Signature::from_bytes(bytes).map_err(|_| {
157 IotaError::InvalidSignature {
158 error: "Cannot parse secp256k1 sig".to_string(),
159 }
160 })?)
161 .into(),
162 )),
163 SignatureScheme::Secp256r1 | SignatureScheme::PasskeyAuthenticator => {
164 Ok(CompressedSignature::Secp256r1(
165 (&Secp256r1Signature::from_bytes(bytes).map_err(|_| {
166 IotaError::InvalidSignature {
167 error: "Cannot parse secp256r1 sig".to_string(),
168 }
169 })?)
170 .into(),
171 ))
172 }
173 _ => Err(IotaError::UnsupportedFeature {
174 error: "Unsupported signature scheme".to_string(),
175 }),
176 }
177 }
178 GenericSignature::ZkLoginAuthenticator(s) => Ok(CompressedSignature::ZkLogin(
179 ZkLoginAuthenticatorAsBytes(s.as_ref().to_vec()),
180 )),
181 GenericSignature::PasskeyAuthenticator(s) => Ok(CompressedSignature::Passkey(
182 PasskeyAuthenticatorAsBytes(s.as_ref().to_vec()),
183 )),
184 _ => Err(IotaError::UnsupportedFeature {
185 error: "Unsupported signature scheme".to_string(),
186 }),
187 }
188 }
189
190 pub fn to_public_key(&self) -> Result<PublicKey, IotaError> {
194 match self {
195 GenericSignature::Signature(s) => {
196 let bytes = s.public_key_bytes();
197 match s.scheme() {
198 SignatureScheme::ED25519 => Ok(PublicKey::Ed25519(
199 (&Ed25519PublicKey::from_bytes(bytes).map_err(|_| {
200 IotaError::KeyConversion("Cannot parse ed25519 pk".to_string())
201 })?)
202 .into(),
203 )),
204 SignatureScheme::Secp256k1 => Ok(PublicKey::Secp256k1(
205 (&Secp256k1PublicKey::from_bytes(bytes).map_err(|_| {
206 IotaError::KeyConversion("Cannot parse secp256k1 pk".to_string())
207 })?)
208 .into(),
209 )),
210 SignatureScheme::Secp256r1 => Ok(PublicKey::Secp256r1(
211 (&Secp256r1PublicKey::from_bytes(bytes).map_err(|_| {
212 IotaError::KeyConversion("Cannot parse secp256r1 pk".to_string())
213 })?)
214 .into(),
215 )),
216 _ => Err(IotaError::UnsupportedFeature {
217 error: "Unsupported signature scheme in MultiSig".to_string(),
218 }),
219 }
220 }
221 GenericSignature::ZkLoginAuthenticator(s) => s.get_pk(),
222 GenericSignature::PasskeyAuthenticator(s) => s.get_pk(),
223 GenericSignature::MoveAuthenticator(_) => Err(IotaError::UnsupportedFeature {
224 error: "Unsupported in MoveAuthenticator".to_string(),
225 }),
226 _ => Err(IotaError::UnsupportedFeature {
227 error: "Unsupported signature scheme".to_string(),
228 }),
229 }
230 }
231}
232
233impl ToFromBytes for GenericSignature {
238 fn from_bytes(bytes: &[u8]) -> Result<Self, FastCryptoError> {
239 match SignatureScheme::from_flag_byte(
240 bytes.first().ok_or(FastCryptoError::InputTooShort(0))?,
241 ) {
242 Ok(x) => match x {
243 SignatureScheme::ED25519
244 | SignatureScheme::Secp256k1
245 | SignatureScheme::Secp256r1 => Ok(GenericSignature::Signature(
246 Signature::from_bytes(bytes).map_err(|_| FastCryptoError::InvalidSignature)?,
247 )),
248 SignatureScheme::MultiSig => {
249 Ok(GenericSignature::MultiSig(MultiSig::from_bytes(bytes)?))
250 }
251 SignatureScheme::ZkLoginAuthenticator => {
252 let zk_login = ZkLoginAuthenticator::from_bytes(bytes)?;
253 Ok(GenericSignature::ZkLoginAuthenticator(zk_login))
254 }
255 SignatureScheme::PasskeyAuthenticator => {
256 let passkey = PasskeyAuthenticator::from_bytes(bytes)?;
257 Ok(GenericSignature::PasskeyAuthenticator(passkey))
258 }
259 SignatureScheme::MoveAuthenticator => {
260 let move_auth = MoveAuthenticator::from_bytes(bytes)?;
261 Ok(GenericSignature::MoveAuthenticator(move_auth))
262 }
263 _ => Err(FastCryptoError::InvalidInput),
264 },
265 Err(_) => Err(FastCryptoError::InvalidInput),
266 }
267 }
268}
269
270impl AsRef<[u8]> for GenericSignature {
272 fn as_ref(&self) -> &[u8] {
273 match self {
274 GenericSignature::MultiSig(s) => s.as_ref(),
275 GenericSignature::Signature(s) => s.as_ref(),
276 GenericSignature::ZkLoginAuthenticator(s) => s.as_ref(),
277 GenericSignature::PasskeyAuthenticator(s) => s.as_ref(),
278 GenericSignature::MoveAuthenticator(s) => s.as_ref(),
279 }
280 }
281}
282
283impl ::serde::Serialize for GenericSignature {
284 fn serialize<S: ::serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
285 if serializer.is_human_readable() {
286 #[derive(serde::Serialize)]
287 struct GenericSignature(String);
288 GenericSignature(self.encode_base64()).serialize(serializer)
289 } else {
290 #[derive(serde::Serialize)]
291 struct GenericSignature<'a>(&'a [u8]);
292 GenericSignature(self.as_ref()).serialize(serializer)
293 }
294 }
295}
296
297impl<'de> ::serde::Deserialize<'de> for GenericSignature {
298 fn deserialize<D: ::serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
299 use serde::de::Error;
300
301 if deserializer.is_human_readable() {
302 #[derive(serde::Deserialize)]
303 struct GenericSignature(String);
304 let s = GenericSignature::deserialize(deserializer)?;
305 Self::decode_base64(&s.0).map_err(::serde::de::Error::custom)
306 } else {
307 #[derive(serde::Deserialize)]
308 struct GenericSignature(Vec<u8>);
309
310 let data = GenericSignature::deserialize(deserializer)?;
311 Self::from_bytes(&data.0).map_err(|e| Error::custom(e.to_string()))
312 }
313 }
314}
315
316impl AuthenticatorTrait for Signature {
319 fn verify_user_authenticator_epoch(&self, _: EpochId, _: Option<EpochId>) -> IotaResult {
320 Ok(())
321 }
322
323 fn verify_claims<T>(
324 &self,
325 value: &IntentMessage<T>,
326 author: IotaAddress,
327 _aux_verify_data: &VerifyParams,
328 _zklogin_inputs_cache: Arc<VerifiedDigestCache<ZKLoginInputsDigest>>,
329 ) -> IotaResult
330 where
331 T: Serialize,
332 {
333 self.verify_secure(value, author, self.scheme())
334 }
335}