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}