1use argon2::Config;
58use citadel_io::tokio;
59use citadel_types::crypto::SecBuffer;
60use futures::Future;
61use rand::rngs::ThreadRng;
62use rand::Rng;
63use serde::{Deserialize, Serialize};
64use std::ops::Deref;
65use std::pin::Pin;
66use std::sync::Arc;
67use std::task::{Context, Poll};
68use tokio::task::{JoinError, JoinHandle};
69
70const ARGON_SALT_LENGTH: usize = 16;
71
72pub struct AsyncArgon {
74 pub task: JoinHandle<ArgonStatus>,
76}
77
78impl AsyncArgon {
79 pub fn hash(password: SecBuffer, settings: ArgonSettings) -> Self {
80 let task = tokio::task::spawn_blocking(move || {
81 match argon2::hash_raw(
82 password.as_ref(),
83 settings.inner.salt.as_slice(),
84 &settings.as_argon_config(),
85 ) {
86 Ok(hashed) => ArgonStatus::HashSuccess(SecBuffer::from(hashed)),
87 Err(err) => ArgonStatus::HashFailed(err.to_string()),
88 }
89 });
90
91 Self { task }
92 }
93
94 pub fn verify(proposed_password: SecBuffer, settings: ServerArgonContainer) -> Self {
95 let task = tokio::task::spawn_blocking(move || {
96 match argon2::verify_raw(
97 proposed_password.as_ref(),
98 settings.settings.inner.salt.as_slice(),
99 settings.hashed_password.as_ref(),
100 &settings.settings.as_argon_config(),
101 ) {
102 Ok(true) => ArgonStatus::VerificationSuccess,
103
104 Ok(false) => ArgonStatus::VerificationFailed(None),
105
106 Err(err) => ArgonStatus::VerificationFailed(Some(err.to_string())),
107 }
108 });
109
110 Self { task }
111 }
112}
113
114#[derive(Clone, Serialize, Deserialize, Debug)]
115pub struct ArgonSettings {
116 inner: Arc<ArgonSettingsInner>,
117}
118
119impl ArgonSettings {
120 pub fn new(
121 ad: Vec<u8>,
122 salt: Vec<u8>,
123 lanes: u32,
124 hash_length: u32,
125 mem_cost: u32,
126 time_cost: u32,
127 secret: Vec<u8>,
128 ) -> Self {
129 Self {
130 inner: Arc::new(ArgonSettingsInner {
131 ad,
132 salt,
133 lanes,
134 hash_length,
135 mem_cost,
136 time_cost,
137 secret,
138 }),
139 }
140 }
141
142 pub fn new_defaults(ad: Vec<u8>) -> Self {
144 Self::new_defaults_with_static_secret(ad, vec![])
145 }
146
147 pub fn new_defaults_with_static_secret(ad: Vec<u8>, secret: Vec<u8>) -> Self {
149 Self::new_gen_salt(
150 ad,
151 DEFAULT_LANES,
152 DEFAULT_HASH_LENGTH,
153 DEFAULT_MEM_COST,
154 DEFAULT_TIME_COST,
155 secret,
156 )
157 }
158
159 pub fn derive_new_with_custom_ad(&self, ad: Vec<u8>) -> Self {
161 Self::new_gen_salt(
162 ad,
163 self.lanes,
164 self.hash_length,
165 self.mem_cost,
166 self.time_cost,
167 self.secret.clone(),
168 )
169 }
170
171 pub fn new_gen_salt(
172 ad: Vec<u8>,
173 lanes: u32,
174 hash_length: u32,
175 mem_cost: u32,
176 time_cost: u32,
177 secret: Vec<u8>,
178 ) -> Self {
179 Self::new(
180 ad,
181 Self::generate_salt().to_vec(),
182 lanes,
183 hash_length,
184 mem_cost,
185 time_cost,
186 secret,
187 )
188 }
189
190 fn generate_salt() -> [u8; ARGON_SALT_LENGTH] {
191 let mut rng = ThreadRng::default();
192 let mut salt: [u8; ARGON_SALT_LENGTH] = Default::default();
193 rng.fill(&mut salt);
194 salt
195 }
196}
197
198impl Deref for ArgonSettings {
199 type Target = ArgonSettingsInner;
200
201 fn deref(&self) -> &Self::Target {
202 self.inner.as_ref()
203 }
204}
205
206#[derive(Serialize, Deserialize, Debug)]
207pub struct ArgonSettingsInner {
208 pub ad: Vec<u8>,
209 pub salt: Vec<u8>,
210 pub lanes: u32,
211 pub hash_length: u32,
212 pub mem_cost: u32,
213 pub time_cost: u32,
214 pub secret: Vec<u8>,
215}
216
217impl ArgonSettings {
218 pub fn as_argon_config(&self) -> Config {
220 Config {
221 ad: self.inner.ad.as_slice(),
222 hash_length: self.inner.hash_length,
223 lanes: self.inner.lanes,
224 mem_cost: self.inner.mem_cost,
225 secret: self.inner.secret.as_slice(),
226 time_cost: self.inner.time_cost,
227 variant: argon2::Variant::Argon2id,
228 version: argon2::Version::Version13,
229 }
230 }
231}
232
233impl Default for ArgonSettings {
234 fn default() -> Self {
235 ArgonSettings::new_defaults(vec![])
236 }
237}
238
239#[derive(Clone, Serialize, Deserialize, Default)]
240pub struct ClientArgonContainer {
241 pub settings: ArgonSettings,
242}
243
244impl From<ArgonSettings> for ClientArgonContainer {
245 fn from(settings: ArgonSettings) -> Self {
246 Self { settings }
247 }
248}
249
250impl ClientArgonContainer {
251 pub async fn hash_insecure_input(&self, input: SecBuffer) -> Option<SecBuffer> {
252 match AsyncArgon::hash(input, self.settings.clone()).await.ok()? {
253 ArgonStatus::HashSuccess(ret) => Some(ret),
254 _ => None,
255 }
256 }
257}
258
259#[derive(Clone, Serialize, Deserialize)]
260pub struct ServerArgonContainer {
261 settings: ArgonSettings,
262 hashed_password: SecBuffer,
263}
264
265impl ServerArgonContainer {
266 pub fn new(settings: ArgonSettings, hashed_password: SecBuffer) -> Self {
267 Self {
268 settings,
269 hashed_password,
270 }
271 }
272}
273
274#[derive(Debug)]
275pub enum ArgonStatus {
276 HashSuccess(SecBuffer),
277 HashFailed(String),
278 VerificationSuccess,
279 VerificationFailed(Option<String>),
280}
281
282#[derive(Clone, Serialize, Deserialize)]
283#[allow(variant_size_differences)]
284pub enum ArgonContainerType {
285 Client(ClientArgonContainer),
286 Server(ServerArgonContainer),
287}
288
289impl ArgonContainerType {
290 pub fn client(&self) -> Option<&ClientArgonContainer> {
291 match self {
292 Self::Client(cl) => Some(cl),
293 _ => None,
294 }
295 }
296
297 pub fn server(&self) -> Option<&ServerArgonContainer> {
298 match self {
299 Self::Server(sv) => Some(sv),
300 _ => None,
301 }
302 }
303
304 pub fn settings(&self) -> &ArgonSettings {
305 match self {
306 Self::Client(cl) => &cl.settings,
307 Self::Server(sv) => &sv.settings,
308 }
309 }
310}
311
312impl Future for AsyncArgon {
313 type Output = Result<ArgonStatus, JoinError>;
314
315 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
316 Pin::new(&mut self.task).poll(cx)
317 }
318}
319
320const DEFAULT_LANES: u32 = 8;
321pub const DEFAULT_HASH_LENGTH: u32 = 32;
322#[cfg(not(debug_assertions))]
323const DEFAULT_MEM_COST: u32 = 1024 * 64;
324#[cfg(debug_assertions)]
325const DEFAULT_MEM_COST: u32 = 1024;
326#[cfg(not(debug_assertions))]
327const DEFAULT_TIME_COST: u32 = 10;
328#[cfg(debug_assertions)]
329const DEFAULT_TIME_COST: u32 = 1;
330
331#[derive(Debug, Clone)]
332pub struct ArgonDefaultServerSettings {
333 pub lanes: u32,
334 pub hash_length: u32,
335 pub mem_cost: u32,
336 pub time_cost: u32,
337 pub secret: Vec<u8>,
338}
339
340impl From<ArgonDefaultServerSettings> for ArgonSettings {
341 fn from(settings: ArgonDefaultServerSettings) -> Self {
342 Self::new_gen_salt(
344 vec![],
345 settings.lanes,
346 settings.hash_length,
347 settings.mem_cost,
348 settings.time_cost,
349 settings.secret,
350 )
351 }
352}
353
354impl Default for ArgonDefaultServerSettings {
355 fn default() -> Self {
356 Self {
357 lanes: DEFAULT_LANES,
358 hash_length: DEFAULT_HASH_LENGTH,
359 mem_cost: DEFAULT_MEM_COST,
360 time_cost: DEFAULT_TIME_COST,
361 secret: vec![],
362 }
363 }
364}