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