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