1use std::fs::File;
11use std::io::{BufReader, BufWriter, Write};
12use std::path::{Path, PathBuf};
13
14use crate::container::{self, KeyDerivation, PayloadType};
15
16use crate::error::{Error, Result};
17
18pub fn encrypt_file(password: &str, input: &Path, output: Option<&Path>) -> Result<PathBuf> {
34 let input_path = input
35 .canonicalize()
36 .map_err(|_| Error::invalid_name(input.display()))?;
37 if !input_path.is_file() {
38 return Err(Error::invalid_name(input.display()));
39 }
40
41 let original_name = input_path
42 .file_name()
43 .map(|n| n.to_string_lossy().to_string())
44 .unwrap_or_else(|| "unknown".into());
45
46 let mut argon_salt = [0u8; container::SALT_LEN];
47 let mut object_salt = [0u8; container::SALT_LEN];
48 crate::crypto::fill_random(&mut argon_salt);
49 crate::crypto::fill_random(&mut object_salt);
50
51 let kd = KeyDerivation::Password {
52 argon_salt,
53 object_salt,
54 params: crate::kdf::KdfParams::new(),
55 };
56
57 let output_path = match output {
58 Some(p) => p.to_path_buf(),
59 None => {
60 use sha2::{Digest, Sha256};
62 let mut hasher = Sha256::new();
63 hasher.update(argon_salt);
64 hasher.update(object_salt);
65 PathBuf::from(format!("{:x}.krf", hasher.finalize()))
66 }
67 };
68
69 let tmp_path = crate::fsutil::sibling_temp_path(&output_path);
70 let result = (|| -> Result<()> {
71 let mut out_file = File::create(&tmp_path)?;
72 crate::fsutil::restrict_perms(&tmp_path);
73
74 let mut src = File::open(&input_path)?;
75 {
76 let mut w = BufWriter::new(&mut out_file);
77 container::write_container(
78 &mut w,
79 &kd,
80 Some(password),
81 None,
82 PayloadType::File,
83 b"",
84 Some(&mut src),
85 Some(&original_name),
86 )?;
87 w.flush()?;
88 }
89 out_file.sync_all()?;
90 drop(out_file);
91
92 #[cfg(windows)]
93 if output_path.exists() {
94 std::fs::remove_file(&output_path)?;
95 }
96 std::fs::rename(&tmp_path, &output_path)?;
97 crate::fsutil::restrict_perms(&output_path);
98 crate::fsutil::sync_dir(output_path.parent().unwrap_or_else(|| Path::new(".")));
99 Ok(())
100 })();
101
102 match result {
103 Ok(()) => Ok(output_path),
104 Err(e) => {
105 let _ = std::fs::remove_file(&tmp_path);
106 Err(e)
107 }
108 }
109}
110
111pub fn decrypt_file(password: &str, input: &Path, output: &Path) -> Result<String> {
122 let file = BufReader::new(File::open(input)?);
123
124 let tmp_path = crate::fsutil::sibling_temp_path(output);
125 let outcome = (|| -> Result<String> {
126 let tmp = File::create(&tmp_path)?;
127 crate::fsutil::restrict_perms(&tmp_path);
128 let mut sink = BufWriter::new(tmp);
129
130 let trailer = container::read_container(
131 file,
132 Some(password),
133 None,
134 PayloadType::File,
135 b"",
136 |chunk| sink.write_all(chunk).map_err(Error::from),
137 )?;
138 sink.flush()?;
139 drop(sink);
140
141 let name = trailer.name.ok_or(Error::MalformedPayload)?;
142
143 #[cfg(windows)]
144 if output.exists() {
145 std::fs::remove_file(output)?;
146 }
147 std::fs::rename(&tmp_path, output)?;
148 crate::fsutil::sync_dir(output.parent().unwrap_or_else(|| Path::new(".")));
149 Ok(name)
150 })();
151
152 match outcome {
153 Ok(name) => Ok(name),
154 Err(e) => {
155 let _ = std::fs::remove_file(&tmp_path);
156 Err(e)
157 }
158 }
159}