diff --git a/core/src/crypto.rs b/core/src/crypto.rs index 7ee4c0e..3246197 100644 --- a/core/src/crypto.rs +++ b/core/src/crypto.rs @@ -1,3 +1,5 @@ +use std::str::FromStr; +use std::iter; use argon2::{ Argon2, PasswordHasher as ArgonPasswordHasher, PasswordVerifier as ArgonPasswordVerifier, }; @@ -27,18 +29,19 @@ pub fn random_8_char_string() -> String { // TODO: No unwrap for spawn_blocking pub async fn hash_password( - password_plaint_text: String, + password_plain_text: String, ) -> Result { let argon_config = Argon2::default(); let hash = tokio::task::spawn_blocking(move || { - let password = password_plaint_text.as_bytes(); + let password = password_plain_text.as_bytes(); let salt = "c2VjcmV0bHl0ZXN0aW5nZXZlcnl0aGluZw"; let encrypted = argon_config.hash_password(password, salt); encrypted }) - .await.unwrap(); + .await + .unwrap(); let result = hash?; @@ -52,25 +55,26 @@ pub async fn verify_password<'a>( ) -> Result { let argon_config = Argon2::default(); - let result: Result = tokio::task::spawn_blocking(move || { - let parsed_hash = argon2::PasswordHash::new(&hash); - match parsed_hash { - Ok(parsed) => { - return Ok(argon_config - .verify_password(password_plaint_text.as_bytes(), &parsed) - .is_ok()) + let result: Result = + tokio::task::spawn_blocking(move || { + let parsed_hash = argon2::PasswordHash::new(&hash); + match parsed_hash { + Ok(parsed) => { + return Ok(argon_config + .verify_password(password_plaint_text.as_bytes(), &parsed) + .is_ok()) + } + Err(error) => return Err(error), } - Err(error) => return Err(error), - } - }) - .await - .unwrap(); + }) + .await + .unwrap(); result } pub async fn encrypt_password( - password_plaint_text: &str, + password_plain_text: &str, key: &str, ) -> Result { let encryptor = age::Encryptor::with_user_passphrase(age::secrecy::Secret::new(key.to_owned())); @@ -79,7 +83,7 @@ pub async fn encrypt_password( let mut encrypt_writer = encryptor.wrap_async_output(&mut encrypt_buffer).await?; encrypt_writer - .write_all(password_plaint_text.as_bytes()) + .write_all(password_plain_text.as_bytes()) .await?; encrypt_writer.flush().await?; @@ -108,3 +112,56 @@ pub async fn decrypt_password( Ok(String::from_utf8(decrypt_buffer)?) } + +pub async fn encrypt_password_with_recipients( + password_plain_text: &str, + recipients: Vec<&str>, +) -> Result { + let public_keys = recipients + .into_iter() + .map(|recipient| { + //TODO: No unwrap + Box::new(age::x25519::Recipient::from_str(recipient).unwrap()) as _ + }) + .collect(); + + let encryptor_option = age::Encryptor::with_recipients(public_keys); + + if let Some(encryptor) = encryptor_option { + let mut encrypt_buffer = Vec::new(); + let mut encrypt_writer = encryptor.wrap_async_output(&mut encrypt_buffer).await?; + + encrypt_writer + .write_all(password_plain_text.as_bytes()) + .await?; + + encrypt_writer.flush().await?; + + encrypt_writer.close().await?; + + Ok(base64::encode(encrypt_buffer)) + } else { + // TODO: Error handling + unreachable!("No recipients provided"); + } +} + +pub async fn decrypt_password_with_private_key( + password_encrypted: &str, + key: &str, +) -> Result> { + let encrypted = base64::decode(password_encrypted)?; + + let decryptor = match age::Decryptor::new_async(&encrypted[..]).await? { + age::Decryptor::Recipients(d) => d, + _ => unreachable!(), + }; + + let mut decrypt_buffer = Vec::new(); + let mut decrypt_writer = + decryptor.decrypt_async(iter::once(&age::x25519::Identity::from_str(key)? as &dyn age::Identity))?; + + decrypt_writer.read_to_end(&mut decrypt_buffer).await?; + + Ok(String::from_utf8(decrypt_buffer)?) +}