use crate::encryption::chunk_job::result::ChunkResult;
use crate::encryption::chunk_job::{
decrypt::DecryptChunkJob, encrypt::EncryptChunkJob, submit_decrypt_chunk, submit_encrypt_chunk,
};
use crate::encryption::encryption_primitives::generate_nonce;
use crate::encryption::file::read_file;
use crate::encryption::shared_cipher;
use crate::errors::EnkryptitError;
use crate::key::EnkryptitKey;
use crate::metadatas::{ArchiveHeader, MetaDatas};
use crate::parallelism::executable::EnkryptitExecutable;
use crate::parallelism::pool::EnkryptitPool;
use crate::types::{CHUNK_SIZE, CompressionType};
use gradient_bar::GradientProgressBar;
use std::fs::File;
use std::io::Read;
use std::io::{BufWriter, Write};
use std::io::{Seek, SeekFrom};
use std::sync::Arc;
pub fn encrypt_multithread_file(
path: &str,
compression: CompressionType,
enkryptit_key: EnkryptitKey,
num_threads: u8,
) -> Result<String, EnkryptitError> {
let pool = EnkryptitPool::<EncryptChunkJob>::new(num_threads as usize)?;
let mut file = read_file(path)?;
let master_nonce = generate_nonce();
let cipher = shared_cipher(enkryptit_key.key_as_ref());
let metadata = MetaDatas::new(enkryptit_key.key_type(), compression, master_nonce).pack()?;
let encrypted_path = format!("{}.encky", path);
let cipherfile = std::fs::File::create(&encrypted_path)?;
let mut writer = BufWriter::new(cipherfile);
let actual_meta_len = metadata.len() as u32;
let header = ArchiveHeader::new(false, actual_meta_len).pack()?;
writer.write_all(&[header.len() as u8])?;
writer.write_all(&header)?;
writer.write_all(&metadata)?;
let arc_compression = Arc::new(compression);
let arc_nonce = Arc::new(master_nonce);
let mut buffer = vec![0u8; CHUNK_SIZE];
let mut step: u64 = 0;
let mut results = Vec::with_capacity(num_threads as usize);
let mut submitted = 0u8;
let pb = GradientProgressBar::with_total_steps(file.estimated_steps, "Encrypting...");
loop {
let bytes_read = file.reader.read(&mut buffer)?;
if bytes_read == 0 {
break;
}
if submitted >= num_threads {
receive_results(&mut results, &pool, num_threads)?;
write_batch(&mut results, &mut writer)?;
submitted = 0;
}
submit_encrypt_chunk(
&pool,
step,
buffer[..bytes_read].to_vec(),
arc_nonce.clone(),
arc_compression.clone(),
cipher.clone(),
)?;
submitted += 1;
step += 1;
pb.update(step);
}
if submitted > 0 {
receive_results(&mut results, &pool, submitted)?;
write_batch(&mut results, &mut writer)?;
}
writer.write_all(b"ENK1END")?;
writer.flush()?;
pb.finish();
Ok(encrypted_path)
}
pub fn decrypt_multithread_file(
path: &str,
payload_offset: u64,
enkryptit_key: EnkryptitKey,
master_nonce: [u8; 24],
compression: CompressionType,
num_threads: u8,
) -> Result<String, EnkryptitError> {
let pool = EnkryptitPool::<DecryptChunkJob>::new(num_threads as usize)?;
let mut file = read_file(path)?;
let plain_path = path.strip_suffix(".encky").unwrap_or(path);
let new_file = std::fs::File::create(plain_path)?;
let mut writer = BufWriter::new(new_file);
file.reader.seek(SeekFrom::Start(payload_offset))?;
let cipher = shared_cipher(enkryptit_key.key_as_ref());
let arc_compression = Arc::new(compression);
let arc_nonce = Arc::new(master_nonce);
let mut step: u64 = 0;
let mut results = Vec::with_capacity(num_threads as usize);
let mut submitted = 0u8;
let pb = GradientProgressBar::with_total_bytes(file.estimated_steps, "Decrypting...");
loop {
let mut len_buf = [0u8; 4];
match file.reader.read_exact(&mut len_buf) {
Ok(_) => {}
Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => {
break;
}
Err(e) => return Err(e.into()),
}
if &len_buf == b"ENK1" {
let mut end = [0u8; 3];
file.reader.read_exact(&mut end)?;
if &end != b"END" {
}
break;
}
let len = u32::from_le_bytes(len_buf) as usize;
let mut payload = vec![0u8; len];
file.reader.read_exact(&mut payload)?;
if submitted >= num_threads {
receive_results(&mut results, &pool, num_threads)?;
write_batch_plain(&mut results, &mut writer)?;
submitted = 0;
}
submit_decrypt_chunk(
&pool,
step,
payload,
arc_nonce.clone(),
arc_compression.clone(),
cipher.clone(),
)?;
submitted += 1;
step += 1;
pb.update(step);
}
if submitted > 0 {
receive_results(&mut results, &pool, submitted)?;
write_batch_plain(&mut results, &mut writer)?;
}
pb.finish();
writer.flush()?;
Ok(plain_path.to_string())
}
pub fn write_batch(
results: &mut Vec<ChunkResult>,
writer: &mut BufWriter<File>,
) -> Result<u64, EnkryptitError> {
results.sort_by_key(|r| r.index);
let mut written = 0u64;
for chunk_result in results.iter() {
let data = &chunk_result.data;
let len = (data.len() as u32).to_le_bytes();
writer.write_all(&len)?;
written += 4;
writer.write_all(data)?;
written += data.len() as u64;
}
results.clear();
Ok(written)
}
pub fn write_batch_plain(
results: &mut Vec<ChunkResult>,
writer: &mut BufWriter<File>,
) -> Result<u64, EnkryptitError> {
results.sort_by_key(|r| r.index);
let mut written = 0u64;
for chunk_result in results.iter() {
writer.write_all(&chunk_result.data)?;
written += chunk_result.data.len() as u64;
}
results.clear();
Ok(written)
}
pub fn receive_results<T: EnkryptitExecutable + Send + 'static>(
results: &mut Vec<T::Output>,
pool: &EnkryptitPool<T>,
num_threads: u8,
) -> Result<(), EnkryptitError> {
for _ in 0..num_threads {
let result = pool.recv()??;
results.push(result);
}
Ok(())
}