Add updated Cargo.toml and crypto.rs with pure-Rust crypto
Some checks failed
CI / test (push) Failing after 2s
Some checks failed
CI / test (push) Failing after 2s
This commit is contained in:
@@ -1,5 +1,7 @@
|
|||||||
use sodiumoxide::crypto::secretbox;
|
use aes_gcm::{Aes256Gcm, Key, Nonce};
|
||||||
use sodiumoxide::crypto::pwhash;
|
use aes_gcm::aead::{Aead, AeadCore, OsRng};
|
||||||
|
use sha2::Sha256;
|
||||||
|
use hkdf::Hkdf;
|
||||||
use base64::{Engine as _, engine::general_purpose::STANDARD};
|
use base64::{Engine as _, engine::general_purpose::STANDARD};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
@@ -27,41 +29,36 @@ impl fmt::Display for CryptoError {
|
|||||||
impl std::error::Error for CryptoError {}
|
impl std::error::Error for CryptoError {}
|
||||||
|
|
||||||
pub struct CryptoManager {
|
pub struct CryptoManager {
|
||||||
key: secretbox::Key,
|
key: Key<Aes256Gcm>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CryptoManager {
|
impl CryptoManager {
|
||||||
pub fn from_password(password: &str, salt: &[u8; pwhash::SALTBYTES]) -> Result<Self, CryptoError> {
|
const KEY_SIZE: usize = 32;
|
||||||
let mut key = secretbox::Key::from_slice(&[0u8; secretbox::KEYBYTES])
|
|
||||||
.ok_or(CryptoError::InvalidKey)?;
|
|
||||||
|
|
||||||
|
pub fn from_password(password: &str, salt: &[u8; 32]) -> Result<Self, CryptoError> {
|
||||||
let password_bytes = password.as_bytes();
|
let password_bytes = password.as_bytes();
|
||||||
|
|
||||||
let derived_key = pwhash::pwhash(
|
let hkdf = Hkdf::<Sha256>::new(Some(salt), password_bytes);
|
||||||
password_bytes,
|
let mut key_bytes = [0u8; Self::KEY_SIZE];
|
||||||
salt,
|
hkdf.expand(&[], &mut key_bytes)
|
||||||
pwhash::Oscillating::interactive().unwrap(),
|
.map_err(|_| CryptoError::KeyDerivationFailed)?;
|
||||||
secretbox::KEYBYTES,
|
|
||||||
).map_err(|_| CryptoError::KeyDerivationFailed)?;
|
|
||||||
|
|
||||||
key.as_mut_slice().copy_from_slice(&derived_key);
|
let key = Key::<Aes256Gcm>::from_slice(&key_bytes);
|
||||||
|
|
||||||
Ok(CryptoManager { key })
|
Ok(CryptoManager { key: *key })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn encrypt(&self, plaintext: &[u8]) -> Result<(Vec<u8>, Vec<u8>), CryptoError> {
|
pub fn encrypt(&self, plaintext: &[u8]) -> Result<(Vec<u8>, Vec<u8>), CryptoError> {
|
||||||
let nonce = secretbox::gen_nonce();
|
let nonce = Aes256Gcm::generate_nonce(OsRng);
|
||||||
let ciphertext = secretbox::seal(plaintext, &nonce, &self.key)
|
let ciphertext = Aes256Gcm::encrypt(&nonce, plaintext, None)
|
||||||
.map_err(|_| CryptoError::EncryptionFailed)?;
|
.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<Vec<u8>, CryptoError> {
|
pub fn decrypt(&self, nonce: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
||||||
let nonce = secretbox::Nonce::from_slice(nonce)
|
let nonce = Nonce::from_slice(nonce);
|
||||||
.ok_or(CryptoError::InvalidNonce)?;
|
let plaintext = Aes256Gcm::decrypt(nonce, ciphertext, None)
|
||||||
|
|
||||||
let plaintext = secretbox::open(ciphertext, &nonce, &self.key)
|
|
||||||
.map_err(|_| CryptoError::DecryptionFailed)?;
|
.map_err(|_| CryptoError::DecryptionFailed)?;
|
||||||
|
|
||||||
Ok(plaintext)
|
Ok(plaintext)
|
||||||
@@ -81,11 +78,11 @@ impl CryptoManager {
|
|||||||
let combined = STANDARD.decode(encrypted_data)
|
let combined = STANDARD.decode(encrypted_data)
|
||||||
.map_err(|_| CryptoError::DecryptionFailed)?;
|
.map_err(|_| CryptoError::DecryptionFailed)?;
|
||||||
|
|
||||||
if combined.len() < secretbox::NONCEBYTES + secretbox::MACBYTES {
|
if combined.len() < 12 + 16 {
|
||||||
return Err(CryptoError::DecryptionFailed);
|
return Err(CryptoError::DecryptionFailed);
|
||||||
}
|
}
|
||||||
|
|
||||||
let nonce_len = secretbox::NONCEBYTES;
|
let nonce_len = 12;
|
||||||
let nonce = &combined[..nonce_len];
|
let nonce = &combined[..nonce_len];
|
||||||
let ciphertext = &combined[nonce_len..];
|
let ciphertext = &combined[nonce_len..];
|
||||||
|
|
||||||
@@ -96,26 +93,25 @@ impl CryptoManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_salt() -> [u8; pwhash::SALTBYTES] {
|
pub fn generate_salt() -> [u8; 32] {
|
||||||
let mut salt = [0u8; pwhash::SALTBYTES];
|
let mut salt = [0u8; 32];
|
||||||
sodiumoxide::init().unwrap();
|
|
||||||
rand::Rng::fill(&mut rand::thread_rng(), &mut salt);
|
rand::Rng::fill(&mut rand::thread_rng(), &mut salt);
|
||||||
salt
|
salt
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn salt_to_base64(salt: &[u8; pwhash::SALTBYTES]) -> String {
|
pub fn salt_to_base64(salt: &[u8; 32]) -> String {
|
||||||
STANDARD.encode(salt)
|
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)
|
let decoded = STANDARD.decode(salt_str)
|
||||||
.map_err(|_| CryptoError::DecryptionFailed)?;
|
.map_err(|_| CryptoError::DecryptionFailed)?;
|
||||||
|
|
||||||
if decoded.len() != pwhash::SALTBYTES {
|
if decoded.len() != 32 {
|
||||||
return Err(CryptoError::DecryptionFailed);
|
return Err(CryptoError::DecryptionFailed);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut salt = [0u8; pwhash::SALTBYTES];
|
let mut salt = [0u8; 32];
|
||||||
salt.copy_from_slice(&decoded);
|
salt.copy_from_slice(&decoded);
|
||||||
Ok(salt)
|
Ok(salt)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user