#[cfg(feature = "encryption")]
use std::num::NonZeroU32;
#[cfg(feature = "encryption")]
use ed25519_dalek::SigningKey as Keypair;
#[cfg(feature = "encryption")]
use ring::rand::SecureRandom;
#[cfg(feature = "encryption")]
use ring::{aead, pbkdf2};
#[cfg(feature = "encryption")]
use crate::constants::{NONCE_LEN, SALT_LEN, TAG_LEN};
#[cfg(feature = "encryption")]
use crate::error::{Result, RialoError};
#[cfg(feature = "encryption")]
pub fn encrypt_keypair(keypair: &Keypair, password: &str) -> Result<Vec<u8>> {
let mut salt = [0u8; SALT_LEN];
ring::rand::SystemRandom::new()
.fill(&mut salt)
.map_err(|_| RialoError::Encryption("Failed to generate salt".to_string()))?;
let mut nonce = [0u8; NONCE_LEN];
ring::rand::SystemRandom::new()
.fill(&mut nonce)
.map_err(|_| RialoError::Encryption("Failed to generate nonce".to_string()))?;
let key = derive_key(password, &salt)?;
let keypair_bytes = keypair.as_bytes();
let nonce_sequence = ring::aead::Nonce::assume_unique_for_key(nonce);
let mut in_out = keypair_bytes.to_vec();
key.seal_in_place_append_tag(nonce_sequence, aead::Aad::empty(), &mut in_out)
.map_err(|_| RialoError::Encryption("Failed to encrypt keypair".to_string()))?;
let mut result = Vec::with_capacity(SALT_LEN + NONCE_LEN + in_out.len());
result.extend_from_slice(&salt);
result.extend_from_slice(&nonce);
result.extend_from_slice(&in_out);
Ok(result)
}
#[cfg(feature = "encryption")]
pub fn decrypt_keypair(encrypted: &[u8], password: &str) -> Result<Keypair> {
if encrypted.len() < SALT_LEN + NONCE_LEN + TAG_LEN {
return Err(RialoError::Encryption("Invalid encrypted data".to_string()));
}
let salt = &encrypted[0..SALT_LEN];
let nonce = &encrypted[SALT_LEN..SALT_LEN + NONCE_LEN];
let ciphertext = &encrypted[SALT_LEN + NONCE_LEN..];
let key = derive_key(password, salt)?;
let mut nonce_array = [0u8; NONCE_LEN];
nonce_array.copy_from_slice(nonce);
let nonce_sequence = ring::aead::Nonce::assume_unique_for_key(nonce_array);
let mut decrypted = ciphertext.to_vec();
let decrypted_bytes = key
.open_in_place(nonce_sequence, aead::Aad::empty(), &mut decrypted)
.map_err(|_| RialoError::Password("Invalid password".to_string()))?;
let secret_key_bytes: [u8; 32] = decrypted_bytes[0..32]
.try_into()
.map_err(|_| RialoError::Encryption("Failed to extract secret key".to_string()))?;
let keypair = Keypair::from_bytes(&secret_key_bytes);
Ok(keypair)
}
#[cfg(feature = "encryption")]
fn derive_key(password: &str, salt: &[u8]) -> Result<ring::aead::LessSafeKey> {
let iterations = NonZeroU32::new(100_000)
.ok_or_else(|| RialoError::Encryption("Invalid iteration count".to_string()))?;
let mut key = [0u8; 32];
pbkdf2::derive(
pbkdf2::PBKDF2_HMAC_SHA256,
iterations,
salt,
password.as_bytes(),
&mut key,
);
let unbound_key = aead::UnboundKey::new(&aead::AES_256_GCM, &key)
.map_err(|_| RialoError::Encryption("Failed to create encryption key".to_string()))?;
Ok(aead::LessSafeKey::new(unbound_key))
}
#[cfg(feature = "encryption")]
pub fn encrypt_string(text: &str, password: &str) -> Result<Vec<u8>> {
let mut salt = [0u8; SALT_LEN];
ring::rand::SystemRandom::new()
.fill(&mut salt)
.map_err(|_| RialoError::Encryption("Failed to generate salt".to_string()))?;
let mut nonce = [0u8; NONCE_LEN];
ring::rand::SystemRandom::new()
.fill(&mut nonce)
.map_err(|_| RialoError::Encryption("Failed to generate nonce".to_string()))?;
let key = derive_key(password, &salt)?;
let text_bytes = text.as_bytes();
let nonce_sequence = ring::aead::Nonce::assume_unique_for_key(nonce);
let mut in_out = text_bytes.to_vec();
key.seal_in_place_append_tag(nonce_sequence, aead::Aad::empty(), &mut in_out)
.map_err(|_| RialoError::Encryption("Failed to encrypt text".to_string()))?;
let mut result = Vec::with_capacity(SALT_LEN + NONCE_LEN + in_out.len());
result.extend_from_slice(&salt);
result.extend_from_slice(&nonce);
result.extend_from_slice(&in_out);
Ok(result)
}
#[cfg(feature = "encryption")]
pub fn decrypt_string(encrypted: &[u8], password: &str) -> Result<String> {
if encrypted.len() < SALT_LEN + NONCE_LEN + TAG_LEN {
return Err(RialoError::Encryption("Invalid encrypted data".to_string()));
}
let salt = &encrypted[0..SALT_LEN];
let nonce = &encrypted[SALT_LEN..SALT_LEN + NONCE_LEN];
let ciphertext = &encrypted[SALT_LEN + NONCE_LEN..];
let key = derive_key(password, salt)?;
let mut nonce_array = [0u8; NONCE_LEN];
nonce_array.copy_from_slice(nonce);
let nonce_sequence = ring::aead::Nonce::assume_unique_for_key(nonce_array);
let mut decrypted = ciphertext.to_vec();
let decrypted_bytes = key
.open_in_place(nonce_sequence, aead::Aad::empty(), &mut decrypted)
.map_err(|_| RialoError::Password("Invalid password".to_string()))?;
String::from_utf8(decrypted_bytes.to_vec())
.map_err(|_| RialoError::Encryption("Failed to decode decrypted data as UTF-8".to_string()))
}