smart_account_auth/
impls.rs

1use core::ops::Deref;
2use strum::IntoDiscriminant;
3use saa_auth::caller::Caller;
4use saa_common::{ensure, AuthError, Binary, CredentialId, Verifiable};
5use crate::{credential::CredentialName, Credential, CredentialData, CredentialInfo};
6
7
8impl From<Caller> for Credential {
9    fn from(c: Caller) -> Self {
10        Credential::Native(c)
11    }
12}
13
14#[cfg(feature = "ethereum")]
15impl From<saa_auth::eth::EthPersonalSign> for Credential {
16    fn from(c: saa_auth::eth::EthPersonalSign) -> Self {
17        Credential::EthPersonalSign(c)
18    }
19}
20
21#[cfg(feature = "cosmos")]
22impl From<saa_auth::cosmos::CosmosArbitrary> for Credential {
23    fn from(c: saa_auth::cosmos::CosmosArbitrary) -> Self {
24        Credential::CosmosArbitrary(c)
25    }
26}
27
28
29
30#[cfg(feature = "ed25519")]
31impl From<saa_curves::ed25519::Ed25519> for Credential {
32    fn from(c: saa_curves::ed25519::Ed25519) -> Self {
33        Credential::Ed25519(c)
34    }
35}
36
37
38
39#[cfg(feature = "passkeys")]
40impl From<saa_auth::passkey::PasskeyCredential> for Credential {
41    fn from(c: saa_auth::passkey::PasskeyCredential) -> Self {
42        Credential::Passkey(c)
43    }
44}
45
46#[cfg(feature = "curves")]
47impl From<saa_curves::secp256k1::Secp256k1> for Credential {
48    fn from(c: saa_curves::secp256k1::Secp256k1) -> Self {
49        Credential::Secp256k1(c)
50    }
51}
52
53#[cfg(feature = "curves")]
54impl From<saa_curves::secp256r1::Secp256r1> for Credential {
55    fn from(c: saa_curves::secp256r1::Secp256r1) -> Self {
56        Credential::Secp256r1(c)
57    }
58}
59
60
61
62impl Deref for Credential {
63    type Target = dyn Verifiable;
64
65    fn deref(&self) -> &Self::Target {
66        match self {
67            Credential::Native(c) => c,
68            #[cfg(feature = "passkeys")]
69            Credential::Passkey(c) => c,
70            #[cfg(feature = "ethereum")]
71            Credential::EthPersonalSign(c) => c,
72            #[cfg(feature = "cosmos")]
73            Credential::CosmosArbitrary(c) => c,
74            #[cfg(feature = "ed25519")]
75            Credential::Ed25519(c) => c,
76            #[cfg(feature = "curves")]
77            curve => {
78                match curve {
79                    Credential::Secp256k1(c) => c,
80                    Credential::Secp256r1(c) => c,
81                    _ => unreachable!(),
82                }
83            },
84        }
85    }
86}
87
88
89impl Credential {
90
91    pub fn name(&self) -> CredentialName {
92        self.discriminant()
93    }
94
95
96    pub fn message(&self) -> Vec<u8> {
97        match self {
98            Credential::Native(_) => Vec::new(),
99            #[cfg(feature = "ethereum")]
100            Credential::EthPersonalSign(c) => c.message.to_vec(),
101            #[cfg(feature = "cosmos")]
102            Credential::CosmosArbitrary(c) => c.message.to_vec(),
103            #[cfg(feature = "ed25519")]
104            Credential::Ed25519(c) => c.message.to_vec(),
105            #[cfg(feature = "passkeys")]
106            Credential::Passkey(c) => c.base64_message_bytes().unwrap(),
107            #[cfg(feature = "curves")]
108            curve => {
109                match curve {
110                    Credential::Secp256k1(c) => c.message.to_vec(),
111                    Credential::Secp256r1(c) => c.message.to_vec(),
112                    Credential::Ed25519(c) => c.message.to_vec(),
113                    _ => unreachable!(),
114                }
115            },
116            
117        }
118    }
119
120    pub fn extension(&self) -> Result<Option<Binary>, AuthError> {
121        #[cfg(all(feature = "passkeys", feature = "wasm"))]
122        if let Credential::Passkey(c) = self {
123            use saa_auth::passkey::*;
124            return Ok(Some(saa_common::to_json_binary(&PasskeyInfo {
125                origin: c.client_data.origin.clone(),
126                cross_origin: c.client_data.cross_origin.clone(),
127                pubkey: c.pubkey.clone().unwrap_or_default(),
128                user_handle: c.user_handle.clone(),
129                authenticator_data: c.authenticator_data.clone(),
130            })?));
131        }
132        Ok(None)
133    }
134
135    pub fn info(&self) -> CredentialInfo {
136        CredentialInfo {
137            name: self.name(),
138            hrp: self.hrp(),
139            extension: self.extension().ok().flatten()
140        }
141    }
142
143    
144}
145
146
147
148
149#[cfg(feature = "traits")]
150use crate::traits::CredentialsWrapper;
151
152#[cfg(feature = "traits")]
153impl crate::traits::CredentialsWrapper for CredentialData {
154    type Credential = Credential;
155
156    fn credentials(&self) -> &Vec<Self::Credential> {
157        &self.credentials
158    }
159}
160
161
162
163impl Verifiable for CredentialData {
164
165    fn id(&self) -> CredentialId {
166        #[cfg(feature = "traits")]
167        return self.primary_id();
168        #[cfg(not(feature = "traits"))]
169        self.credentials.first().unwrap().id().clone()
170
171    }
172
173    fn validate(&self) -> Result<(), AuthError> {
174        let creds = &self.credentials;
175        let using_caller = self.use_native.unwrap_or(false);
176
177        let (min_len, max_len) = if using_caller {
178            let count = creds
179                .iter()
180                .filter(|c| c.discriminant() == CredentialName::Native)
181                .count();
182            ensure!(count == 1, AuthError::generic("Native caller is set but wasn't passed by environment"));
183            (0, 256)
184        } else {
185            (1, 255)
186        };
187    
188        if creds.len() < min_len {
189            return Err(AuthError::NoCredentials);
190        } else if creds.len() > max_len {
191            return Err(AuthError::Generic(format!("Too many credentials: {}", creds.len())));
192        }
193
194        if let Some(index) = self.primary_index {
195            let len = creds.len() + if using_caller { 1 } else { 0 };
196            ensure!((index as usize) < len, AuthError::generic(
197                format!("Primary index {} is out of bounds", index)
198            ));
199        }
200        creds.iter().try_for_each(|c| c.validate())
201    }
202
203
204    #[cfg(feature = "native")]
205    fn verify(&self) -> Result<(), AuthError> {
206        self.credentials.iter().try_for_each(|c| c.verify())
207    }
208
209
210    #[cfg(feature = "wasm")]
211    fn verify_cosmwasm(&self,  api : &dyn saa_common::wasm::Api) -> Result<(), AuthError>  {
212        self.credentials.iter().try_for_each(|c| c.verify_cosmwasm(api))
213    }
214
215}
216
217
218
219
220
221impl CredentialData {
222
223    fn cred_index(&self, name: CredentialName, id: Option<CredentialId>) -> Option<usize> {
224        self.credentials.iter()
225            .position(|c| c.name() == name && 
226                    id.as_ref()
227                        .map(|i| c.id() == *i)
228                        .unwrap_or(true)
229            )
230    }
231
232    /// Check whether with_caller flag is set and then ether ignore the arguemnt and return a copy
233    /// or constuct a new wrapper with the credential being set
234    /// @param cal: native caller of the environment
235    /// @return: checked wrapper and a flag indicating whether the copy deviated from the original Self
236    pub fn with_native<C: Into::<Caller>> (&self, cal: C) -> Self {
237        if !self.use_native.unwrap_or(false) {
238            return self.clone()
239        }
240        let caller : Caller = cal.into();
241        let mut credentials = self.credentials.clone();
242
243        match self.cred_index(CredentialName::Native, Some(caller.0.clone())) {
244            Some(index) => credentials[index] = caller.into(),
245            None => credentials.push(caller.into())
246        };
247        Self { 
248            credentials, 
249            use_native: Some(true),
250            primary_index: self.primary_index
251        }
252    }
253
254
255}
256
257