rencfs/lib.rs
1#![feature(test)]
2#![feature(error_generic_member_access)]
3#![feature(seek_stream_len)]
4#![feature(box_into_inner)]
5//! # Encrypted File System
6//!
7//! An encrypted file system that mounts with FUSE on Linux. It can be used to create encrypted directories.
8//!
9//! # Usage
10//!
11//! It can be used a library to create an encrypted file system or mount it with FUSE.
12//!
13//! This crate also contains examples and a `main.rs` file that can be used as examples on how to run the encrypted file system from the command line.
14//! Documentation for that can be found [here](https://github.com/radumarias/rencfs#command-line-tool).
15//!
16//! In the following example, we will see how we can use it as a library.
17//!
18//! ## Using [`mount::create_mount_point`] on Linux
19//!
20//! ### Example
21//!
22//! ```no_run
23//! use std::env::args;
24//! use std::path::Path;
25//! use std::io;
26//! use tracing::info;
27//!
28//! use anyhow::Result;
29//! use shush_rs::SecretString;
30//!
31//! use rencfs::encryptedfs::PasswordProvider;
32//! use rencfs::mount::create_mount_point;
33//! use rencfs::mount::MountPoint;
34//!
35//! /// This will mount and expose the mount point until you press `Enter`, then it will umount and close the program.
36//! #[tokio::main]
37//! #[allow(clippy::type_complexity)]
38//! async fn main() -> Result<()> {
39//! tracing_subscriber::fmt().init();
40//!
41//! let mut args = args();
42//! args.next(); // skip program name
43//! let mount_path = args.next().expect("mount_path expected");
44//! let data_path = args.next().expect("data_path expected");
45//! use rencfs::crypto::Cipher;
46//!
47//! struct PasswordProviderImpl {}
48//! impl PasswordProvider for PasswordProviderImpl {
49//! fn get_password(&self) -> Option<SecretString> {
50//! // placeholder password, use some secure way to get the password like with [keyring](https://crates.io/crates/keyring) crate
51//! Some(SecretString::new(Box::new(String::from("pass42"))))
52//! }
53//! }
54//! let mount_point = create_mount_point(
55//! Path::new(&mount_path),
56//! Path::new(&data_path),
57//! Box::new(PasswordProviderImpl {}),
58//! Cipher::ChaCha20Poly1305,
59//! false,
60//! false,
61//! false,
62//! );
63//! let handle = mount_point.mount().await?;
64//! let mut buffer = String::new();
65//! io::stdin().read_line(&mut buffer)?;
66//! info!("Unmounting...");
67//! info!("Bye!");
68//! handle.umount().await?;
69//!
70//! Ok(())
71//! }
72//! ```
73//!
74//! ## Or directly work with [`encryptedfs::EncryptedFs`]
75//!
76//! You need to specify several parameters to create an encrypted file system:
77//! - `data_dir`: The directory where the file system will be mounted.
78//! - `password`: The password to encrypt/decrypt the data.
79//! - `Cipher`: The encryption algorithm to use.
80//!
81//! Currently, it supports these ciphers [Cipher](crypto::Cipher).
82//!
83//! ### Example
84//!
85//! ```
86//! #![allow(unused_imports)]
87//! use std::fs;
88//! use shush_rs::SecretString;
89//! use rencfs::encryptedfs::{EncryptedFs, FileType, PasswordProvider, CreateFileAttr};
90//! use rencfs::crypto::Cipher;
91//! use anyhow::Result;
92//! use std::path::Path;
93//! use rencfs::encryptedfs::write_all_string_to_fs;
94//!
95//! const ROOT_INODE: u64 = 1;
96//!
97//! struct PasswordProviderImpl {}
98//! impl PasswordProvider for PasswordProviderImpl {
99//! fn get_password(&self) -> Option<SecretString> {
100//! // placeholder password, use some secure way to get the password like with [keyring](https://crates.io/crates/keyring) crate
101//! Some(SecretString::new(Box::new(String::from("pass42"))))
102//! }
103//! }
104//!
105//! #[tokio::main]
106//! async fn main() -> Result<()> {
107//! tracing_subscriber::fmt().init();
108//!
109//! let data_dir = Path::new("/tmp/rencfs_data_test").to_path_buf();
110//! let _ = fs::remove_dir_all(data_dir.to_str().unwrap());
111//! let cipher = Cipher::ChaCha20Poly1305;
112//! let mut fs = EncryptedFs::new(data_dir.clone(), Box::new(PasswordProviderImpl{}), cipher, false).await?;
113//!
114//! let file1 = SecretString::new(Box::new(String::from("file-1")));
115//! let (fh, attr) = fs.create(ROOT_INODE, &file1, file_attr(), false, true).await?;
116//! let data = "Hello, world!";
117//! write_all_string_to_fs( &fs, attr.ino, 0,data, fh).await?;
118//! fs.flush(fh).await?;
119//! fs.release(fh).await?;
120//! let fh = fs.open(attr.ino, true, false).await?;
121//! let mut buf = vec![0; data.len()];
122//! fs.read(attr.ino, 0, &mut buf, fh).await?;
123//! fs.release(fh).await?;
124//! assert_eq!(data, String::from_utf8(buf)?);
125//! fs::remove_dir_all(data_dir)?;
126//!
127//! Ok(())
128//! }
129//!
130//! fn file_attr() -> CreateFileAttr {
131//! CreateFileAttr {
132//! kind: FileType::RegularFile,
133//! perm: 0o644,
134//! uid: 0,
135//! gid: 0,
136//! rdev: 0,
137//! flags: 0,
138//! }
139//! }
140//! ```
141//! ## Change password from code
142//!
143//! ### Example
144//!
145//! ```no_run
146//! use rencfs::crypto::Cipher;
147//! use rencfs::encryptedfs::{EncryptedFs, FsError};
148//! use shush_rs::SecretString;
149//! use std::env::args;
150//! use std::path::Path;
151//!
152//! #[tokio::main]
153//! async fn main() {
154//! tracing_subscriber::fmt().init();
155//!
156//! let mut args = args();
157//! let _ = args.next(); // skip the program name
158//! let data_dir = args.next().expect("data_dir is missing");
159//!
160//! match EncryptedFs::passwd(
161//! Path::new(&data_dir),
162//! SecretString::new(Box::new(String::from("old-pass"))),
163//! SecretString::new(Box::new(String::from("new-pass"))),
164//! Cipher::ChaCha20Poly1305,
165//! )
166//! .await
167//! {
168//! Ok(_) => println!("Password changed successfully"),
169//! Err(FsError::InvalidPassword) => println!("Invalid old password"),
170//! Err(FsError::InvalidDataDirStructure) => println!("Invalid structure of data directory"),
171//! Err(err) => println!("Error: {err}"),
172//! }
173//! }
174//! ```
175//! ## Change password from CLI using [rpassword](https://crates.io/crates/rpassword) crate
176//!
177//! ### Example
178//!
179//! ```no_run
180//! use std::env::args;
181//! use std::io;
182//! use std::io::Write;
183//!
184//! use rpassword::read_password;
185//! use shush_rs::{ExposeSecret, SecretString};
186//! use tracing::{error, info};
187//!
188//! use rencfs::encryptedfs::{EncryptedFs, FsError};
189//! #[tokio::main]
190//! async fn main() {
191//! tracing_subscriber::fmt().init();
192//!
193//! let mut args = args();
194//! let _ = args.next(); // skip the program name
195//! let data_dir = args.next().expect("data_dir is missing");
196//!
197//! use std::path::Path;
198//! // read password from stdin
199//! use rencfs::crypto::Cipher;
200//! print!("Enter old password: ");
201//! io::stdout().flush().unwrap();
202//! let old_password = SecretString::new(Box::new(read_password().unwrap()));
203//! print!("Enter new password: ");
204//! io::stdout().flush().unwrap();
205//! let new_password = SecretString::new(Box::new(read_password().unwrap()));
206//! print!("Confirm new password: ");
207//! io::stdout().flush().unwrap();
208//! let new_password2 = SecretString::new(Box::new(read_password().unwrap()));
209//! if new_password.expose_secret() != new_password2.expose_secret() {
210//! error!("Passwords do not match");
211//! return;
212//! }
213//! println!("Changing password...");
214//! match EncryptedFs::passwd(
215//! Path::new(&data_dir),
216//! old_password,
217//! new_password,
218//! Cipher::ChaCha20Poly1305,
219//! )
220//! .await
221//! {
222//! Ok(_) => info!("Password changed successfully"),
223//! Err(FsError::InvalidPassword) => error!("Invalid old password"),
224//! Err(FsError::InvalidDataDirStructure) => error!("Invalid structure of data directory"),
225//! Err(err) => error!("Error: {err}"),
226//! }
227//! }
228//! ```
229//!
230//! ## Encrypted Writer and Reader
231//!
232//! We also expose a Writer and Reader in encrypted format, which implements [`std::io::Write`], [`std::io::Read`] and [`std::io::Seek`].
233//! You can wrap any [`std::io::Write`] and [`std::io::Read`], like a file, to write and read encrypted content.
234//! This is using [ring](https://crates.io/crates/ring) crate to handle encryption.
235//!
236//! ### Example
237//! ```no_run
238//! use anyhow::Result;
239//! use rand_core::RngCore;
240//! use std::env::args;
241//! use std::fs::File;
242//! use std::io;
243//! use std::io::Write;
244//! use std::path::Path;
245//! use std::sync::Arc;
246//!
247//! use shush_rs::SecretVec;
248//! use tracing::info;
249//!
250//! use rencfs::crypto;
251//! use rencfs::crypto::write::CryptoWrite;
252//! use rencfs::crypto::Cipher;
253//!
254//! fn main() -> Result<()> {
255//! tracing_subscriber::fmt().init();
256//!
257//! let cipher = Cipher::ChaCha20Poly1305;
258//! let mut key = vec![0; cipher.key_len()];
259//! crypto::create_rng().fill_bytes(key.as_mut_slice());
260//! let key = SecretVec::from(key);
261//!
262//! let mut args = args();
263//! // skip the program name
264//! let _ = args.next();
265//! // will encrypt this file
266//! let path_in = args.next().expect("path_in is missing");
267//! // will save it in the same directory with .enc suffix
268//! let out = Path::new(&path_in).to_path_buf().with_extension("enc");
269//! if out.exists() {
270//! std::fs::remove_file(&out)?;
271//! }
272//!
273//! let mut file = File::open(path_in.clone())?;
274//! let mut writer = crypto::create_write(File::create(out.clone())?, cipher, &key);
275//! info!("encrypt file");
276//! io::copy(&mut file, &mut writer).unwrap();
277//! writer.finish()?;
278//!
279//! let mut reader = crypto::create_read(File::open(out)?, cipher, &key);
280//! info!("read file and compare hash to original one");
281//! let hash1 = crypto::hash_reader(&mut File::open(path_in)?)?;
282//! let hash2 = crypto::hash_reader(&mut reader)?;
283//! assert_eq!(hash1, hash2);
284//!
285//! Ok(())
286//! }
287//! ```
288extern crate test;
289
290use std::sync::LazyLock;
291
292pub mod arc_hashmap;
293pub mod async_util;
294pub mod crypto;
295pub mod encryptedfs;
296pub mod expire_value;
297pub mod fs_util;
298pub mod log;
299pub mod mount;
300pub mod stream_util;
301pub(crate) mod test_common;
302
303#[allow(unreachable_code)]
304pub static UID: LazyLock<u32> = LazyLock::new(|| {
305 #[cfg(any(target_os = "linux", target_os = "macos"))]
306 {
307 return unsafe { libc::getuid() };
308 }
309 0
310});
311
312#[allow(unreachable_code)]
313pub static GID: LazyLock<u32> = LazyLock::new(|| {
314 #[cfg(any(target_os = "linux", target_os = "macos"))]
315 {
316 return unsafe { libc::getgid() };
317 }
318 0
319});
320
321#[allow(unreachable_code)]
322#[must_use]
323pub const fn is_debug() -> bool {
324 #[cfg(debug_assertions)]
325 {
326 return true;
327 }
328 false
329}