diff --git a/app/api-token-vault/src/crypto.rs b/app/api-token-vault/src/crypto.rs index 6ecbbb7..5026e77 100644 --- a/app/api-token-vault/src/crypto.rs +++ b/app/api-token-vault/src/crypto.rs @@ -1,5 +1,7 @@ -use sodiumoxide::crypto::secretbox; -use sodiumoxide::crypto::pwhash; +use aes_gcm::{Aes256Gcm, Key, Nonce}; +use aes_gcm::aead::{Aead, AeadCore, OsRng}; +use sha2::Sha256; +use hkdf::Hkdf; use base64::{Engine as _, engine::general_purpose::STANDARD}; use std::fmt; @@ -27,41 +29,36 @@ impl fmt::Display for CryptoError { impl std::error::Error for CryptoError {} pub struct CryptoManager { - key: secretbox::Key, + key: Key, } impl CryptoManager { - pub fn from_password(password: &str, salt: &[u8; pwhash::SALTBYTES]) -> Result { - let mut key = secretbox::Key::from_slice(&[0u8; secretbox::KEYBYTES]) - .ok_or(CryptoError::InvalidKey)?; - + const KEY_SIZE: usize = 32; + + pub fn from_password(password: &str, salt: &[u8; 32]) -> Result { let password_bytes = password.as_bytes(); - let derived_key = pwhash::pwhash( - password_bytes, - salt, - pwhash::Oscillating::interactive().unwrap(), - secretbox::KEYBYTES, - ).map_err(|_| CryptoError::KeyDerivationFailed)?; + let hkdf = Hkdf::::new(Some(salt), password_bytes); + let mut key_bytes = [0u8; Self::KEY_SIZE]; + hkdf.expand(&[], &mut key_bytes) + .map_err(|_| CryptoError::KeyDerivationFailed)?; - key.as_mut_slice().copy_from_slice(&derived_key); + let key = Key::::from_slice(&key_bytes); - Ok(CryptoManager { key }) + Ok(CryptoManager { key: *key }) } pub fn encrypt(&self, plaintext: &[u8]) -> Result<(Vec, Vec), CryptoError> { - let nonce = secretbox::gen_nonce(); - let ciphertext = secretbox::seal(plaintext, &nonce, &self.key) + let nonce = Aes256Gcm::generate_nonce(OsRng); + let ciphertext = Aes256Gcm::encrypt(&nonce, plaintext, None) .map_err(|_| CryptoError::EncryptionFailed)?; - Ok((nonce.as_ref().to_vec(), ciphertext)) + Ok((nonce.to_vec(), ciphertext)) } pub fn decrypt(&self, nonce: &[u8], ciphertext: &[u8]) -> Result, CryptoError> { - let nonce = secretbox::Nonce::from_slice(nonce) - .ok_or(CryptoError::InvalidNonce)?; - - let plaintext = secretbox::open(ciphertext, &nonce, &self.key) + let nonce = Nonce::from_slice(nonce); + let plaintext = Aes256Gcm::decrypt(nonce, ciphertext, None) .map_err(|_| CryptoError::DecryptionFailed)?; Ok(plaintext) @@ -81,11 +78,11 @@ impl CryptoManager { let combined = STANDARD.decode(encrypted_data) .map_err(|_| CryptoError::DecryptionFailed)?; - if combined.len() < secretbox::NONCEBYTES + secretbox::MACBYTES { + if combined.len() < 12 + 16 { return Err(CryptoError::DecryptionFailed); } - let nonce_len = secretbox::NONCEBYTES; + let nonce_len = 12; let nonce = &combined[..nonce_len]; let ciphertext = &combined[nonce_len..]; @@ -96,26 +93,25 @@ impl CryptoManager { } } -pub fn generate_salt() -> [u8; pwhash::SALTBYTES] { - let mut salt = [0u8; pwhash::SALTBYTES]; - sodiumoxide::init().unwrap(); +pub fn generate_salt() -> [u8; 32] { + let mut salt = [0u8; 32]; rand::Rng::fill(&mut rand::thread_rng(), &mut salt); salt } -pub fn salt_to_base64(salt: &[u8; pwhash::SALTBYTES]) -> String { +pub fn salt_to_base64(salt: &[u8; 32]) -> String { STANDARD.encode(salt) } -pub fn salt_from_base64(salt_str: &str) -> Result<[u8; pwhash::SALTBYTES], CryptoError> { +pub fn salt_from_base64(salt_str: &str) -> Result<[u8; 32], CryptoError> { let decoded = STANDARD.decode(salt_str) .map_err(|_| CryptoError::DecryptionFailed)?; - if decoded.len() != pwhash::SALTBYTES { + if decoded.len() != 32 { return Err(CryptoError::DecryptionFailed); } - let mut salt = [0u8; pwhash::SALTBYTES]; + let mut salt = [0u8; 32]; salt.copy_from_slice(&decoded); Ok(salt) }