smart_account_auth/
impls.rs1use 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 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