mirror of
https://github.com/danbulant/Portfolio
synced 2026-05-27 22:12:25 +00:00
feat: ultra fast AES encryption
This commit is contained in:
parent
9cffa8db9f
commit
9221ed0d64
3 changed files with 137 additions and 23 deletions
|
|
@ -26,6 +26,7 @@ infer = "^0.9"
|
||||||
|
|
||||||
# crypto
|
# crypto
|
||||||
rand = "^0.8"
|
rand = "^0.8"
|
||||||
|
aes-gcm = { version = "^0.10", features = ["std"] }
|
||||||
argon2 = { version = "^0.4", features = ["std"] }
|
argon2 = { version = "^0.4", features = ["std"] }
|
||||||
age = { version = "^0.9", features = ["async"] }
|
age = { version = "^0.9", features = ["async"] }
|
||||||
secrecy = { version = "^0.8" }
|
secrecy = { version = "^0.8" }
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
use aes_gcm::aead::Aead;
|
||||||
|
use aes_gcm::{KeyInit};
|
||||||
use argon2::{
|
use argon2::{
|
||||||
Argon2, PasswordHasher as ArgonPasswordHasher, PasswordVerifier as ArgonPasswordVerifier,
|
Argon2, PasswordHasher as ArgonPasswordHasher, PasswordVerifier as ArgonPasswordVerifier,
|
||||||
};
|
};
|
||||||
|
|
@ -74,7 +76,56 @@ pub async fn verify_password<'a>(
|
||||||
Ok(result?)
|
Ok(result?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn convert_key_aes256(key: &str) -> Vec<u8> {
|
||||||
|
const REQUIRED_KEY_BYTES: usize = 32;
|
||||||
|
//const REQUIRED_NONCE_BYTES: usize = 32;
|
||||||
|
|
||||||
|
let key_len = key.as_bytes().len();
|
||||||
|
let multiplied_key: String = key.repeat((REQUIRED_KEY_BYTES / key_len) + 1);
|
||||||
|
|
||||||
|
let key = multiplied_key.as_bytes().to_vec();
|
||||||
|
|
||||||
|
key
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn encrypt_password(
|
pub async fn encrypt_password(
|
||||||
|
password_plain_text: String,
|
||||||
|
key: String,
|
||||||
|
) -> Result<String, Box<dyn std::error::Error>> {
|
||||||
|
let hash = tokio::task::spawn_blocking(move || {
|
||||||
|
let aes_key_nonce = convert_key_aes256(&key);
|
||||||
|
|
||||||
|
let nonce = aes_gcm::Nonce::from_slice(&aes_key_nonce[..12]);
|
||||||
|
|
||||||
|
let cipher = aes_gcm::Aes256Gcm::new_from_slice(&aes_key_nonce[..32]).unwrap();
|
||||||
|
|
||||||
|
let res = cipher.encrypt(nonce, password_plain_text.as_bytes());
|
||||||
|
res
|
||||||
|
})
|
||||||
|
.await??;
|
||||||
|
|
||||||
|
Ok(base64::encode(hash))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn decrypt_password(password_cipher_text: String, key: String) -> Result<String, Box<dyn std::error::Error>> {
|
||||||
|
let input = base64::decode(password_cipher_text).unwrap();
|
||||||
|
let plain = tokio::task::spawn_blocking(move || {
|
||||||
|
let aes_key_nonce = convert_key_aes256(&key);
|
||||||
|
|
||||||
|
let nonce = aes_gcm::Nonce::from_slice(&aes_key_nonce[..12]);
|
||||||
|
let cipher = aes_gcm::Aes256Gcm::new_from_slice(&aes_key_nonce[..32]).unwrap();
|
||||||
|
|
||||||
|
let res = cipher.decrypt(nonce, &*input);
|
||||||
|
|
||||||
|
res
|
||||||
|
}).await??;
|
||||||
|
|
||||||
|
Ok(String::from_utf8(plain).unwrap())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated(note = "Too slow, use AES instead")]
|
||||||
|
pub async fn encrypt_password_age(
|
||||||
password_plain_text: &str,
|
password_plain_text: &str,
|
||||||
key: &str,
|
key: &str,
|
||||||
) -> Result<String, age::EncryptError> {
|
) -> Result<String, age::EncryptError> {
|
||||||
|
|
@ -94,7 +145,8 @@ pub async fn encrypt_password(
|
||||||
Ok(base64::encode(encrypt_buffer))
|
Ok(base64::encode(encrypt_buffer))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn decrypt_password(
|
#[deprecated(note = "Too slow, use AES instead")]
|
||||||
|
pub async fn decrypt_password_age(
|
||||||
password_encrypted: &str,
|
password_encrypted: &str,
|
||||||
key: &str,
|
key: &str,
|
||||||
) -> Result<String, Box<dyn std::error::Error>> {
|
) -> Result<String, Box<dyn std::error::Error>> {
|
||||||
|
|
@ -297,12 +349,21 @@ mod tests {
|
||||||
assert!(result);
|
assert!(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_convert_key_aes256() {
|
||||||
|
const KEY: &str = "test";
|
||||||
|
|
||||||
|
let key = super::convert_key_aes256(KEY);
|
||||||
|
|
||||||
|
assert!(key.len() >= 32);
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_encrypt_password_is_valid_base64() {
|
async fn test_encrypt_password_is_valid_base64() {
|
||||||
const PASSWORD: &str = "test";
|
const PASSWORD: &str = "test";
|
||||||
const KEY: &str = "test";
|
const KEY: &str = "testtesttesttesttesttest";
|
||||||
|
|
||||||
let encrypted = super::encrypt_password(PASSWORD, KEY).await.unwrap();
|
let encrypted = super::encrypt_password(PASSWORD.to_string(), KEY.to_string()).await.unwrap();
|
||||||
|
|
||||||
assert!(base64::decode(encrypted).is_ok());
|
assert!(base64::decode(encrypted).is_ok());
|
||||||
}
|
}
|
||||||
|
|
@ -312,9 +373,33 @@ mod tests {
|
||||||
const PASSWORD: &str = "test";
|
const PASSWORD: &str = "test";
|
||||||
const KEY: &str = "test";
|
const KEY: &str = "test";
|
||||||
|
|
||||||
let encrypted = super::encrypt_password(PASSWORD, KEY).await.unwrap();
|
let encrypted = super::encrypt_password(PASSWORD.to_string(), KEY.to_string()).await.unwrap();
|
||||||
|
|
||||||
let decrypted = super::decrypt_password(&encrypted, KEY).await.unwrap();
|
let decrypted = super::decrypt_password(encrypted, KEY.to_string()).await.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(PASSWORD, decrypted);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_encrypt_password_age_is_valid_base64() {
|
||||||
|
const PASSWORD: &str = "test";
|
||||||
|
const KEY: &str = "testtesttesttesttesttest";
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
|
let encrypted = super::encrypt_password_age(PASSWORD, KEY).await.unwrap();
|
||||||
|
|
||||||
|
assert!(base64::decode(encrypted).is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_encrypt_decrypt_age_password() {
|
||||||
|
const PASSWORD: &str = "test";
|
||||||
|
const KEY: &str = "test";
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
|
let encrypted = super::encrypt_password_age(PASSWORD, KEY).await.unwrap();
|
||||||
|
#[allow(deprecated)]
|
||||||
|
let decrypted = super::decrypt_password_age(&encrypted, KEY).await.unwrap();
|
||||||
|
|
||||||
assert_eq!(PASSWORD, decrypted);
|
assert_eq!(PASSWORD, decrypted);
|
||||||
}
|
}
|
||||||
|
|
@ -404,30 +489,52 @@ mod tests {
|
||||||
let mut plain_file = async_tempfile::TempFile::new().await.unwrap();
|
let mut plain_file = async_tempfile::TempFile::new().await.unwrap();
|
||||||
let mut encrypted_file = async_tempfile::TempFile::new().await.unwrap();
|
let mut encrypted_file = async_tempfile::TempFile::new().await.unwrap();
|
||||||
|
|
||||||
tokio::io::AsyncWriteExt::write_all(&mut plain_file, PASSWORD.as_bytes()).await.unwrap();
|
tokio::io::AsyncWriteExt::write_all(&mut plain_file, PASSWORD.as_bytes())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
encrypted_file.sync_all().await.unwrap();
|
encrypted_file.sync_all().await.unwrap();
|
||||||
|
|
||||||
assert_eq!(tokio::fs::read_to_string(&plain_file.file_path()).await.unwrap(), PASSWORD);
|
assert_eq!(
|
||||||
|
tokio::fs::read_to_string(&plain_file.file_path())
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
PASSWORD
|
||||||
|
);
|
||||||
|
|
||||||
super::encrypt_file_with_recipients(&plain_file.file_path(), &encrypted_file.file_path(), vec![PUBLIC_KEY])
|
super::encrypt_file_with_recipients(
|
||||||
|
&plain_file.file_path(),
|
||||||
|
&encrypted_file.file_path(),
|
||||||
|
vec![PUBLIC_KEY],
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
encrypted_file.sync_all().await.unwrap();
|
encrypted_file.sync_all().await.unwrap();
|
||||||
|
|
||||||
let mut buffer = [0; 21];
|
let mut buffer = [0; 21];
|
||||||
|
|
||||||
tokio::io::AsyncReadExt::read(&mut encrypted_file, &mut buffer).await.unwrap();
|
tokio::io::AsyncReadExt::read(&mut encrypted_file, &mut buffer)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(&buffer, b"age-encryption.org/v1");
|
assert_eq!(&buffer, b"age-encryption.org/v1");
|
||||||
|
|
||||||
let decrypted_file = async_tempfile::TempFile::new().await.unwrap();
|
let decrypted_file = async_tempfile::TempFile::new().await.unwrap();
|
||||||
|
|
||||||
super::decrypt_file_with_private_key(&encrypted_file.file_path(), &decrypted_file.file_path(), PRIVATE_KEY)
|
super::decrypt_file_with_private_key(
|
||||||
|
&encrypted_file.file_path(),
|
||||||
|
&decrypted_file.file_path(),
|
||||||
|
PRIVATE_KEY,
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
decrypted_file.sync_all().await.unwrap();
|
decrypted_file.sync_all().await.unwrap();
|
||||||
|
|
||||||
assert_eq!(tokio::fs::read_to_string(&decrypted_file.file_path()).await.unwrap(), PASSWORD);
|
assert_eq!(
|
||||||
|
tokio::fs::read_to_string(&decrypted_file.file_path())
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
PASSWORD
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
|
@ -441,13 +548,19 @@ mod tests {
|
||||||
let mut plain_file = async_tempfile::TempFile::new().await.unwrap();
|
let mut plain_file = async_tempfile::TempFile::new().await.unwrap();
|
||||||
let encrypted_file = async_tempfile::TempFile::new().await.unwrap();
|
let encrypted_file = async_tempfile::TempFile::new().await.unwrap();
|
||||||
|
|
||||||
tokio::io::AsyncWriteExt::write_all(&mut plain_file, PASSWORD.as_bytes()).await.unwrap();
|
tokio::io::AsyncWriteExt::write_all(&mut plain_file, PASSWORD.as_bytes())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let plain_buffer = tokio::fs::read(&plain_file.file_path()).await.unwrap();
|
let plain_buffer = tokio::fs::read(&plain_file.file_path()).await.unwrap();
|
||||||
|
|
||||||
assert_eq!(String::from_utf8(plain_buffer.clone()).unwrap(), PASSWORD);
|
assert_eq!(String::from_utf8(plain_buffer.clone()).unwrap(), PASSWORD);
|
||||||
|
|
||||||
super::encrypt_file_with_recipients(&plain_file.file_path(), &encrypted_file.file_path(), vec![PUBLIC_KEY])
|
super::encrypt_file_with_recipients(
|
||||||
|
&plain_file.file_path(),
|
||||||
|
&encrypted_file.file_path(),
|
||||||
|
vec![PUBLIC_KEY],
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ impl Mutation {
|
||||||
// TODO: unwrap pro testing..
|
// TODO: unwrap pro testing..
|
||||||
let hashed_password = hash_password(plain_text_password.to_string()).await.unwrap();
|
let hashed_password = hash_password(plain_text_password.to_string()).await.unwrap();
|
||||||
let (pubkey, priv_key_plain_text) = crypto::create_identity();
|
let (pubkey, priv_key_plain_text) = crypto::create_identity();
|
||||||
let encrypted_priv_key = crypto::encrypt_password(&priv_key_plain_text, &plain_text_password.to_string()).await.unwrap();
|
let encrypted_priv_key = crypto::encrypt_password_age(&priv_key_plain_text, &plain_text_password.to_string()).await.unwrap();
|
||||||
|
|
||||||
|
|
||||||
candidate::ActiveModel {
|
candidate::ActiveModel {
|
||||||
|
|
@ -101,7 +101,7 @@ mod tests {
|
||||||
|
|
||||||
let encrypted_message = crypto::encrypt_password_with_recipients(&secret_message, vec![&candidate.public_key]).await.unwrap();
|
let encrypted_message = crypto::encrypt_password_with_recipients(&secret_message, vec![&candidate.public_key]).await.unwrap();
|
||||||
|
|
||||||
let private_key_plain_text = crypto::decrypt_password(&candidate.private_key, &plain_text_password).await.unwrap();
|
let private_key_plain_text = crypto::decrypt_password_age(&candidate.private_key, &plain_text_password).await.unwrap();
|
||||||
|
|
||||||
let decrypted_message = crypto::decrypt_password_with_private_key(&encrypted_message, &private_key_plain_text).await.unwrap();
|
let decrypted_message = crypto::decrypt_password_with_private_key(&encrypted_message, &private_key_plain_text).await.unwrap();
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue