use std::fs::File;
use std::io::{BufReader, BufWriter, Read, Write};
use std::path::Path;
use crate::aead::Algorithm;
use crate::error::{Error, Result};
use super::frame::HEADER_LEN;
use super::{StreamDecryptor, StreamEncryptor};
const IO_BUFFER_LEN: usize = 64 * 1024;
pub fn encrypt_file(
input_path: impl AsRef<Path>,
output_path: impl AsRef<Path>,
key: &[u8],
algorithm: Algorithm,
) -> Result<()> {
let input = File::open(input_path.as_ref()).map_err(|_| Error::Mac("stream: open input"))?;
let output =
File::create(output_path.as_ref()).map_err(|_| Error::Mac("stream: create output"))?;
let mut reader = BufReader::with_capacity(IO_BUFFER_LEN, input);
let mut writer = BufWriter::with_capacity(IO_BUFFER_LEN, output);
let (mut enc, header) = StreamEncryptor::new(key, algorithm)?;
writer
.write_all(&header)
.map_err(|_| Error::Mac("stream: write header"))?;
let mut io_buf = alloc::vec![0u8; IO_BUFFER_LEN];
loop {
let n = reader
.read(&mut io_buf)
.map_err(|_| Error::Mac("stream: read input"))?;
if n == 0 {
break;
}
let encrypted = enc.update(&io_buf[..n])?;
writer
.write_all(&encrypted)
.map_err(|_| Error::Mac("stream: write chunk"))?;
}
let tail = enc.finalize()?;
writer
.write_all(&tail)
.map_err(|_| Error::Mac("stream: write final chunk"))?;
writer
.flush()
.map_err(|_| Error::Mac("stream: flush output"))?;
Ok(())
}
pub fn decrypt_file(
input_path: impl AsRef<Path>,
output_path: impl AsRef<Path>,
key: &[u8],
) -> Result<()> {
let input = File::open(input_path.as_ref()).map_err(|_| Error::Mac("stream: open input"))?;
let output =
File::create(output_path.as_ref()).map_err(|_| Error::Mac("stream: create output"))?;
let mut reader = BufReader::with_capacity(IO_BUFFER_LEN, input);
let mut writer = BufWriter::with_capacity(IO_BUFFER_LEN, output);
let mut header = [0u8; HEADER_LEN];
reader
.read_exact(&mut header)
.map_err(|_| Error::Mac("stream: read header"))?;
let mut dec = StreamDecryptor::new(key, &header)?;
let mut io_buf = alloc::vec![0u8; IO_BUFFER_LEN];
loop {
let n = reader
.read(&mut io_buf)
.map_err(|_| Error::Mac("stream: read input"))?;
if n == 0 {
break;
}
let plaintext = dec.update(&io_buf[..n])?;
writer
.write_all(&plaintext)
.map_err(|_| Error::Mac("stream: write plaintext"))?;
}
let tail = dec.finalize()?;
writer
.write_all(&tail)
.map_err(|_| Error::Mac("stream: write final plaintext"))?;
writer
.flush()
.map_err(|_| Error::Mac("stream: flush output"))?;
Ok(())
}