ssec_cli/
enc.rs

1use ssec_core::Encrypt;
2use futures_util::StreamExt;
3use tokio::io::AsyncWriteExt;
4use zeroize::Zeroizing;
5use rand::rngs::OsRng;
6use indicatif::{ProgressBar, ProgressStyle};
7use crate::cli::EncArgs;
8use crate::BAR_STYLE;
9
10const SPINNER_STYLE: &str = "{spinner} deriving encryption key";
11
12pub async fn enc(args: EncArgs) -> Result<(), ()> {
13	let password = tokio::task::spawn_blocking(move || {
14		rpassword::prompt_password("password: ")
15			.map(Zeroizing::new)
16	}).await.unwrap().map_err(|e| {
17		eprintln!("failed to read password interactively: {e}");
18	})?;
19
20	let f_in = tokio::fs::File::open(&args.in_file).await.map_err(|e| {
21		eprintln!("failed to open file {:?}: {e}", args.in_file);
22	})?;
23	let f_in_metadata = f_in.metadata().await.map_err(|e| {
24		eprintln!("failed to get metadata of input file: {e}");
25	})?;
26
27	if !f_in_metadata.is_file() {
28		eprintln!("ERROR: input file {:?} is not, in fact, a file", args.in_file);
29		return Err(());
30	}
31
32	let f_in_len = f_in_metadata.len();
33	if f_in_len == 0 {
34		eprintln!("input file {:?} is empty", args.in_file);
35		return Ok(());
36	}
37
38	let progress = ProgressBar::new(f_in_len);
39	progress.set_style(ProgressStyle::with_template(BAR_STYLE).unwrap());
40	let f_in = progress.wrap_async_read(f_in);
41
42	let s = tokio_util::io::ReaderStream::new(f_in);
43	let mut enc = tokio::task::spawn_blocking(move || {
44		let spinner = ProgressBar::new_spinner();
45		spinner.set_style(ProgressStyle::with_template(SPINNER_STYLE).unwrap());
46		spinner.enable_steady_tick(std::time::Duration::from_millis(100));
47		Encrypt::new_uncompressed(s, password.as_bytes(), &mut OsRng, f_in_len)
48	}).await.unwrap().unwrap();
49
50	let mut f_out = match args.out_file {
51		Some(out_file) => {
52			tokio::fs::OpenOptions::new()
53				.create(true)
54				.write(true)
55				.truncate(true)
56				.open(&out_file).await.map_err(|e| {
57					eprintln!("failed to open specified outout file {out_file:?}: {e}");
58				}).map(tokio::io::BufWriter::new)?
59		},
60		None => {
61			let out_name = format!("{}.ssec", args.in_file.display());
62			tokio::fs::File::create_new(&out_name).await.map_err(|e| {
63				eprintln!("failed to create new output file {out_name:?}: {e}");
64			}).map(tokio::io::BufWriter::new)?
65		}
66	};
67
68	progress.reset();
69
70	while let Some(bytes) = enc.next().await {
71		f_out.write_all(&bytes.unwrap()).await.unwrap();
72	}
73
74	f_out.shutdown().await.unwrap();
75
76	Ok(())
77}