From 8d3b89b965ed4e4955966fbd301a5980f7dd18d2 Mon Sep 17 00:00:00 2001 From: Radu Marias Date: Sat, 4 May 2024 05:35:41 +0300 Subject: [PATCH] use random generator for nonce generator --- Cargo.lock | 1 + Cargo.toml | 1 + examples/ring_crypto.rs | 6 +- src/crypto.rs | 102 +++++++++----- src/crypto/decryptor.rs | 14 +- src/crypto/encryptor.rs | 23 +++- src/encryptedfs.rs | 243 ++++++++++++++++++++-------------- src/encryptedfs/moved_test.rs | 2 +- src/encryptedfs_fuse3.rs | 2 +- src/main.rs | 6 +- 10 files changed, 250 insertions(+), 150 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 44dfe0e2..301f673d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1493,6 +1493,7 @@ dependencies = [ "mio", "num-format", "rand", + "rand_chacha", "retainer", "ring", "rpassword", diff --git a/Cargo.toml b/Cargo.toml index c07ec754..6ad75a48 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,7 @@ retainer = "0.3.0" num-format = "0.4.4" ring = "0.17.8" hex = "0.4.3" +rand_chacha = "0.3.1" [package.metadata.aur] depends = ["fuse3"] diff --git a/examples/ring_crypto.rs b/examples/ring_crypto.rs index 19e44e68..a46522ab 100644 --- a/examples/ring_crypto.rs +++ b/examples/ring_crypto.rs @@ -60,15 +60,15 @@ fn main() { let key = SecretVec::new(key); let key_store = KeyStore::new(key); println!("hash {:?}", key_store.hash); - let mut writer = crypto::create_crypto_writer(OpenOptions::new().read(true).write(true).create(true).open(path.clone()).unwrap(), - &cipher, &derived_key); + let mut writer = crypto::create_writer(OpenOptions::new().read(true).write(true).create(true).open(path.clone()).unwrap(), + &cipher, &derived_key, 42_u64); bincode::serialize_into(&mut writer, &key_store).unwrap(); writer.flush().unwrap(); writer.finish().unwrap(); // read key - let reader = crypto::create_crypto_reader(File::open(path).unwrap(), &cipher, &derived_key); + let reader = crypto::create_reader(File::open(path).unwrap(), &cipher, &derived_key); let key_store: KeyStore = bincode::deserialize_from(reader).map_err(|_| FsError::InvalidPassword).unwrap(); println!("key {:?}", key_store.key.expose_secret()); println!("hash {:?}", key_store.hash); diff --git a/src/crypto.rs b/src/crypto.rs index 7dca1667..16c955e7 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -1,11 +1,15 @@ use std::fs::OpenOptions; use std::io::{Read, Write}; use std::io; +use std::num::ParseIntError; use std::path::PathBuf; use argon2::Argon2; +use argon2::password_hash::rand_core::RngCore; +use base64::DecodeError; use hex::FromHexError; use num_format::{Locale, ToFormattedString}; +use rand::thread_rng; use ring::aead::{AES_256_GCM, CHACHA20_POLY1305}; use secrecy::{ExposeSecret, SecretString, SecretVec}; use serde::{Deserialize, Serialize}; @@ -29,6 +33,8 @@ pub enum Cipher { Aes256Gcm, } +pub const ENCRYPT_FILENAME_OVERHEAD_CHARS: usize = 8; + #[derive(Debug, Error)] pub enum Error { // #[error("cryptostream error: {source}")] @@ -43,37 +49,50 @@ pub enum Error { source: io::Error, // backtrace: Backtrace, }, - #[error("hex error: {source}")] - Hex { + #[error("from hex error: {source}")] + FromHexError { #[from] source: FromHexError, // backtrace: Backtrace, }, - #[error("crypto error: {0}")] - Generic(String), + #[error("hex decode: {source}")] + DecodeError { + #[from] + source: DecodeError, + // backtrace: Backtrace, + }, + #[error("parse int: {source}")] + ParseIntError { + #[from] + source: ParseIntError, + // backtrace: Backtrace, + }, + #[error("generic error: {0}")] + Generic(&'static str), + #[error("generic error: {0}")] + GenericString(String), } pub type Result = std::result::Result; -pub fn create_crypto_writer(writer: W, cipher: &Cipher, key: &SecretVec) -> impl CryptoWriter { - // create_cryptostream_crypto_writer(file, cipher, key) - create_ring_crypto_writer(writer, cipher, key) +pub fn create_writer(writer: W, cipher: &Cipher, key: &SecretVec, nonce_seed: u64) -> impl CryptoWriter { + create_ring_writer(writer, cipher, key, nonce_seed) } -fn create_ring_crypto_writer(writer: W, cipher: &Cipher, key: &SecretVec) -> RingCryptoWriter { +fn create_ring_writer(writer: W, cipher: &Cipher, key: &SecretVec, nonce_seed: u64) -> RingCryptoWriter { let algorithm = match cipher { Cipher::ChaCha20 => &CHACHA20_POLY1305, Cipher::Aes256Gcm => &AES_256_GCM, }; - RingCryptoWriter::new(writer, algorithm, &key.expose_secret()) + RingCryptoWriter::new(writer, algorithm, &key.expose_secret(), nonce_seed) } -fn create_ring_crypto_reader(reader: R, cipher: &Cipher, key: &SecretVec) -> RingCryptoReader { +fn create_ring_reader(reader: R, cipher: &Cipher, key: &SecretVec, nonce_seed: u64) -> RingCryptoReader { let algorithm = match cipher { Cipher::ChaCha20 => &CHACHA20_POLY1305, Cipher::Aes256Gcm => &AES_256_GCM, }; - RingCryptoReader::new(reader, algorithm, &key.expose_secret()) + RingCryptoReader::new(reader, algorithm, &key.expose_secret(), nonce_seed) } // fn _create_cryptostream_crypto_writer(mut file: File, cipher: &Cipher, key: &SecretVec) -> impl CryptoWriter { @@ -94,9 +113,8 @@ fn create_ring_crypto_reader(reader: R, cipher: &Cipher, // } #[instrument(skip(reader, key))] -pub fn create_crypto_reader(reader: R, cipher: &Cipher, key: &SecretVec) -> impl CryptoReader { - // create_cryptostream_crypto_reader(file, cipher, &key) - create_ring_crypto_reader(reader, cipher, &key) +pub fn create_reader(reader: R, cipher: &Cipher, key: &SecretVec, nonce_seed: u64) -> impl CryptoReader { + create_ring_reader(reader, cipher, &key, nonce_seed) } // fn _create_cryptostream_crypto_reader(mut file: File, cipher: &Cipher, key: &SecretVec) -> CryptostreamCryptoReader { @@ -122,28 +140,35 @@ pub fn create_crypto_reader(reader: R, cipher: &Cipher, k // CryptostreamCryptoReader::new(file, get_cipher(cipher), &key.expose_secret(), &iv).unwrap() // } -pub fn encrypt_string(s: &SecretString, cipher: &Cipher, key: &SecretVec) -> Result { +pub fn encrypt_string_with_nonce_seed(s: &SecretString, cipher: &Cipher, key: &SecretVec, nonce_seed: u64) -> Result { let mut cursor = io::Cursor::new(vec![]); - - let mut writer = create_crypto_writer(cursor, cipher, key); - writer.write_all(s.expose_secret().as_bytes()).unwrap(); - writer.flush().unwrap(); + let mut writer = create_writer(cursor, cipher, key, nonce_seed); + writer.write_all(s.expose_secret().as_bytes())?; + writer.flush()?; cursor = writer.finish()?.unwrap(); - Ok(hex::encode(&cursor.into_inner())) + let v = cursor.into_inner(); + Ok(format!("{}.{}", base64::encode(v), nonce_seed)) } pub fn decrypt_string(s: &str, cipher: &Cipher, key: &SecretVec) -> Result { - let vec = hex::decode(s)?; + // extract nonce seed + if !s.contains(".") { + return Err(Error::Generic("nonce seed is missing")); + } + let nonce_seed = s.split('.').last().unwrap().parse::()?; + let s = s.split('.').next().unwrap(); + + let vec = base64::decode(s)?; let cursor = io::Cursor::new(vec); - let mut reader = create_crypto_reader(cursor, cipher, key); + let mut reader = create_reader(cursor, cipher, key, nonce_seed); let mut decrypted = String::new(); reader.read_to_string(&mut decrypted)?; Ok(SecretString::new(decrypted)) } -pub fn decrypt_and_unnormalize_end_file_name(name: &str, cipher: &Cipher, key: &SecretVec) -> Result { - // let name = String::from(name).replace("|", "/"); +pub fn decrypt_file_name(name: &str, cipher: &Cipher, key: &SecretVec) -> Result { + let name = String::from(name).replace("|", "/"); decrypt_string(&name, cipher, key) } @@ -156,18 +181,22 @@ pub fn derive_key(password: &SecretString, cipher: &Cipher, salt: SecretVec) }; dk.resize(key_len, 0); Argon2::default().hash_password_into(password.expose_secret().as_bytes(), salt.expose_secret(), &mut dk) - .map_err(|err| Error::Generic(err.to_string()))?; + .map_err(|err| Error::GenericString(err.to_string()))?; Ok(SecretVec::new(dk)) } -pub fn encrypt_file_name(name: &SecretString, cipher: &Cipher, key: &SecretVec) -> FsResult { +pub fn encrypt_file_name_with_nonce_seed(name: &SecretString, cipher: &Cipher, key: &SecretVec, nonce_seed: u64) -> FsResult { if name.expose_secret() != "$." && name.expose_secret() != "$.." { let normalized_name = SecretString::new(name.expose_secret().replace("/", " ").replace("\\", " ")); - let encrypted = encrypt_string(&normalized_name, cipher, key)?; - // encrypted = encrypted.replace("/", "|"); - return Ok(encrypted); + let mut encrypted = encrypt_string_with_nonce_seed(&normalized_name, cipher, key, nonce_seed)?; + encrypted = encrypted.replace("/", "|"); + Ok(encrypted) + } else { + // add nonce seed + let mut name = name.expose_secret().clone(); + name.push_str(&nonce_seed.to_string()); + Ok(name) } - Ok(name.expose_secret().to_owned()) } pub fn hash(data: &[u8]) -> [u8; 32] { @@ -182,14 +211,14 @@ pub fn hash_secret(data: &SecretString) -> SecretVec { /// Copy from `pos` position in file `len` bytes #[instrument(skip(w, key), fields(pos = pos.to_formatted_string(& Locale::en), len = len.to_formatted_string(& Locale::en)))] -pub fn copy_from_file_exact(w: &mut impl Write, pos: u64, len: u64, cipher: &Cipher, key: &SecretVec, file: PathBuf) -> io::Result<()> { +pub fn copy_from_file_exact(w: &mut impl Write, pos: u64, len: u64, cipher: &Cipher, key: &SecretVec, nonce_seed: u64, file: PathBuf) -> io::Result<()> { debug!(""); if len == 0 { // no-op return Ok(()); } // create a new reader by reading from the beginning of the file - let mut reader = create_crypto_reader(OpenOptions::new().read(true).open(file)?, cipher, key); + let mut reader = create_reader(OpenOptions::new().read(true).open(file)?, cipher, key, nonce_seed); // move read position to the write position stream_util::read_seek_forward_exact(&mut reader, pos)?; @@ -198,3 +227,12 @@ pub fn copy_from_file_exact(w: &mut impl Write, pos: u64, len: u64, cipher: &Cip reader.finish(); Ok(()) } + + +pub fn extract_nonce_from_encrypted_string(name: &str) -> Result { + if !name.contains(".") { + return Err(Error::Generic("nonce seed is missing")); + } + let nonce_seed = name.split('.').last().unwrap().parse::()?; + Ok(nonce_seed) +} \ No newline at end of file diff --git a/src/crypto/decryptor.rs b/src/crypto/decryptor.rs index 960cc5d9..67c9f0a0 100644 --- a/src/crypto/decryptor.rs +++ b/src/crypto/decryptor.rs @@ -2,6 +2,7 @@ use std::io; use std::io::{Read, Seek, SeekFrom}; use ring::aead::{Aad, Algorithm, BoundKey, OpeningKey, UnboundKey}; +use tracing::{error, instrument}; use crate::crypto::buf_mut::BufMut; use crate::crypto::encryptor::{BUF_SIZE, CounterNonceSequence}; @@ -51,9 +52,9 @@ pub struct RingCryptoReader { } impl RingCryptoReader { - pub fn new<'a: 'static>(r: R, algorithm: &'a Algorithm, key: &[u8]) -> Self { + pub fn new<'a: 'static>(r: R, algorithm: &'a Algorithm, key: &[u8], nonce_seed: u64) -> Self { let unbound_key = UnboundKey::new(algorithm, &key).unwrap(); - let nonce_sequence = CounterNonceSequence(1); + let nonce_sequence = CounterNonceSequence::new(nonce_seed); let opening_key = OpeningKey::new(unbound_key, nonce_sequence); let buf = BufMut::new(vec![0; BUF_SIZE + algorithm.tag_len()]); Self { @@ -65,6 +66,7 @@ impl RingCryptoReader { } impl Read for RingCryptoReader { + #[instrument(name = "RingCryptoReader:read", skip(self, buf))] fn read(&mut self, buf: &mut [u8]) -> io::Result { // first try to read remaining decrypted data let len = self.buf.read(buf)?; @@ -94,7 +96,11 @@ impl Read for RingCryptoReader { return Ok(0); } let mut data = &mut buffer[..len]; - let plaintext = self.opening_key.open_within(Aad::empty(), &mut data, 0..).unwrap(); + let res = self.opening_key.open_within(Aad::empty(), &mut data, 0..); + if res.is_err() { + error!("error opening in place: {:?}", res); + } + let plaintext = res.unwrap(); plaintext.len() }; self.buf.seek(SeekFrom::Start(pos as u64)).unwrap(); @@ -104,7 +110,7 @@ impl Read for RingCryptoReader { } impl Seek for RingCryptoReader { - fn seek(&mut self, pos: SeekFrom) -> io::Result { + fn seek(&mut self, _pos: SeekFrom) -> io::Result { todo!() } } diff --git a/src/crypto/encryptor.rs b/src/crypto/encryptor.rs index 26570967..5d6a06a1 100644 --- a/src/crypto/encryptor.rs +++ b/src/crypto/encryptor.rs @@ -1,5 +1,7 @@ use std::io; use std::io::{BufWriter, Write}; +use rand_chacha::ChaCha8Rng; +use rand_chacha::rand_core::{RngCore, SeedableRng}; use ring::aead::{Aad, Algorithm, BoundKey, Nonce, NONCE_LEN, NonceSequence, SealingKey, UnboundKey}; use ring::error::Unspecified; @@ -55,10 +57,10 @@ pub struct RingCryptoWriter { } impl RingCryptoWriter { - pub fn new<'a: 'static>(w: W, algorithm: &'a Algorithm, key: &[u8]) -> Self { + pub fn new<'a: 'static>(w: W, algorithm: &'a Algorithm, key: &[u8], nonce_seed: u64) -> Self { // todo: param for start nonce sequence let unbound_key = UnboundKey::new(&algorithm, &key).unwrap(); - let nonce_sequence = CounterNonceSequence(1); + let nonce_sequence = CounterNonceSequence::new(nonce_seed); let sealing_key = SealingKey::new(unbound_key, nonce_sequence); let buf = BufMut::new(vec![0; BUF_SIZE]); Self { @@ -118,18 +120,27 @@ impl CryptoWriter for RingCryptoWriter { } } -pub(crate) struct CounterNonceSequence(pub(crate) u32); +pub(crate) struct CounterNonceSequence{ + rng: ChaCha8Rng, +} + +impl CounterNonceSequence { + pub fn new(seed: u64) -> Self { + Self { + rng: ChaCha8Rng::seed_from_u64(seed), + } + } +} impl NonceSequence for CounterNonceSequence { // called once for each seal operation fn advance(&mut self) -> Result { let mut nonce_bytes = vec![0; NONCE_LEN]; - let bytes = self.0.to_be_bytes(); - nonce_bytes[8..].copy_from_slice(&bytes); + let bytes = self.rng.next_u64().to_le_bytes(); + nonce_bytes[4..].copy_from_slice(&bytes); // println!("nonce_bytes = {}", hex::encode(&nonce_bytes)); - self.0 += 1; // advance the counter Nonce::try_assume_unique_for_key(&nonce_bytes) } } \ No newline at end of file diff --git a/src/encryptedfs.rs b/src/encryptedfs.rs index 8113ec3d..c7b6f450 100644 --- a/src/encryptedfs.rs +++ b/src/encryptedfs.rs @@ -4,6 +4,7 @@ use std::collections::{HashMap, HashSet}; use std::fmt::Debug; use std::fs::{File, OpenOptions, ReadDir}; use std::io::{Read, Write}; +use std::num::ParseIntError; use std::ops::Deref; use std::path::PathBuf; use std::str::FromStr; @@ -25,7 +26,7 @@ use tracing::{debug, error, instrument, warn}; use crate::{crypto, stream_util}; use crate::arc_hashmap::{ArcHashMap, Guard}; -use crate::crypto::Cipher; +use crate::crypto::{Cipher, extract_nonce_from_encrypted_string}; use crate::crypto::decryptor::CryptoReader; use crate::crypto::encryptor::CryptoWriter; use crate::expire_value::{ExpireValue, Provider}; @@ -218,60 +219,52 @@ pub enum FsError { source: io::Error, // backtrace: Backtrace, }, - #[error("serialize error: {source}")] SerializeError { #[from] source: bincode::Error, // backtrace: Backtrace, }, - #[error("item not found")] - NotFound(String), - + NotFound(&'static str), #[error("inode not found")] InodeNotFound, - #[error("invalid input")] - InvalidInput(String), - + InvalidInput(&'static str), #[error("invalid node type")] InvalidInodeType, - #[error("invalid file handle")] InvalidFileHandle, - #[error("already exists")] AlreadyExists, - #[error("already open for write")] AlreadyOpenForWrite, - #[error("not empty")] NotEmpty, - #[error("other")] - Other(String), - + Other(&'static str), #[error("invalid password")] InvalidPassword, - #[error("invalid structure of data directory")] InvalidDataDirStructure, - #[error("crypto error: {source}")] Crypto { #[from] source: crypto::Error, // backtrace: Backtrace, }, - #[error("keyring error: {source}")] Keyring { #[from] source: keyring::Error, // backtrace: Backtrace, }, + #[error("parse int error: {source}")] + ParseIntError { + #[from] + source: ParseIntError, + // backtrace: Backtrace, + }, } #[derive(Debug, Clone)] @@ -373,24 +366,52 @@ impl Iterator for DirectoryEntryIterator { let file = file.unwrap(); let name = entry.file_name().to_string_lossy().to_string(); + let nonce_seed; let name = { - if name == "$." { + if name.starts_with("$.") { + // extract nonce seed + if name.len() < 3 { + return Some(Err(FsError::InvalidInput("nonce seed is missing from filename"))); + } + let res = name.split(".").last().unwrap().parse::(); + if let Err(err) = res { + error!(err = %err, "parsing nonce seed"); + return Some(Err(err.into())); + } + nonce_seed = res.unwrap(); Some(SecretString::from_str(".").unwrap()) - } else if name == "$.." { + } else if name.starts_with("$..") { + // extract nonce seed + if name.len() < 4 { + return Some(Err(FsError::InvalidInput("nonce seed is missing from filename"))); + } + let res = name.split("..").last().unwrap().parse::(); + if let Err(err) = res { + error!(err = %err, "parsing nonce seed"); + return Some(Err(err.into())); + } + nonce_seed = res.unwrap(); Some(SecretString::from_str("..").unwrap()) } else { - crypto::decrypt_and_unnormalize_end_file_name(&name, &self.1, &self.2).map_err(|err| { - error!(err = %err, "decrypting and unnormalizing end file name"); + // extract nonce seed + let res = extract_nonce_from_encrypted_string(&name); + if let Err(err) = res { + error!(err = %err, "parsing nonce seed"); + return Some(Err(FsError::from(err))); + } + nonce_seed = res.unwrap(); + crypto::decrypt_file_name(&name, &self.1, &self.2).map_err(|err| { + error!(err = %err, "decrypting file name"); err }).ok() } }; if name.is_none() { - return Some(Err(FsError::InvalidInput("invalid file name".to_string()))); + return Some(Err(FsError::InvalidInput("invalid file name"))); } let name = name.unwrap(); - let res: bincode::Result<(u64, FileType)> = bincode::deserialize_from(crypto::create_crypto_reader(file, &self.1, &self.2)); + let res: bincode::Result<(u64, FileType)> = bincode::deserialize_from(crypto::create_reader(file, &self.1, &self.2, nonce_seed)); if let Err(e) = res { return Some(Err(e.into())); } @@ -426,24 +447,53 @@ impl Iterator for DirectoryEntryPlusIterator { } let file = file.unwrap(); let name = entry.file_name().to_string_lossy().to_string(); + + let nonce_seed; let name = { - if name == "$." { + if name.starts_with("$.") { + // extract nonce seed + if name.len() < 3 { + return Some(Err(FsError::InvalidInput("nonce seed is missing from filename"))); + } + let res = name.split(".").last().unwrap().parse::(); + if let Err(err) = res { + error!(err = %err, "parsing nonce seed"); + return Some(Err(err.into())); + } + nonce_seed = res.unwrap(); Some(SecretString::from_str(".").unwrap()) - } else if name == "$.." { + } else if name.starts_with("$..") { + // extract nonce seed + if name.len() < 4 { + return Some(Err(FsError::InvalidInput("nonce seed is missing from filename"))); + } + let res = name.split("..").last().unwrap().parse::(); + if let Err(err) = res { + error!(err = %err, "parsing nonce seed"); + return Some(Err(err.into())); + } + nonce_seed = res.unwrap(); Some(SecretString::from_str("..").unwrap()) } else { - crypto::decrypt_and_unnormalize_end_file_name(&name, &self.2, &self.3).map_err(|err| { - error!(err = %err, "decrypting and unnormalizing end file name"); + // extract nonce seed + let res = extract_nonce_from_encrypted_string(&name); + if let Err(err) = res { + error!(err = %err, "parsing nonce seed"); + return Some(Err(FsError::from(err))); + } + nonce_seed = res.unwrap(); + crypto::decrypt_file_name(&name, &self.2, &self.3).map_err(|err| { + error!(err = %err, "decrypting file name"); err }).ok() } }; if name.is_none() { - return Some(Err(FsError::InvalidInput("invalid file name".to_string()))); + return Some(Err(FsError::InvalidInput("invalid file name"))); } let name = name.unwrap(); - let res: bincode::Result<(u64, FileType)> = bincode::deserialize_from(crypto::create_crypto_reader(file, &self.2, &self.3)); + let res: bincode::Result<(u64, FileType)> = bincode::deserialize_from(crypto::create_reader(file, &self.2, &self.3, nonce_seed)); if let Err(e) = res { error!(err = %e, "deserializing directory entry"); return Some(Err(e.into())); @@ -460,7 +510,7 @@ impl Iterator for DirectoryEntryPlusIterator { return Some(Err(e.into())); } let file = file.unwrap(); - let attr = bincode::deserialize_from(crypto::create_crypto_reader(file, &self.2, &self.3)); + let attr = bincode::deserialize_from(crypto::create_reader(file, &self.2, &self.3, ino)); if let Err(e) = attr { error!(err = %e, "deserializing file attr"); return Some(Err(e.into())); @@ -516,7 +566,7 @@ enum ReadHandleContextOperation<'a> { ino: u64, lock: Guard>, }, - RecreateDecryptor { + RecreateReader { existing: MutexGuard<'a, ReadHandleContext>, }, } @@ -525,7 +575,7 @@ impl ReadHandleContextOperation<'_> { fn get_ino(&self) -> u64 { match *self { ReadHandleContextOperation::Create { ino, .. } => ino, - ReadHandleContextOperation::RecreateDecryptor { ref existing } => existing.ino, + ReadHandleContextOperation::RecreateReader { ref existing } => existing.ino, } } } @@ -535,7 +585,7 @@ enum WriteHandleContextOperation<'a> { ino: u64, lock: Guard>, }, - RecreateEncryptor { + RecreateWriter { existing: MutexGuard<'a, WriteHandleContext>, reset_size: bool, }, @@ -545,7 +595,7 @@ impl WriteHandleContextOperation<'_> { fn get_ino(&self) -> u64 { match *self { WriteHandleContextOperation::Create { ino, .. } => ino, - WriteHandleContextOperation::RecreateEncryptor { ref existing, .. } => existing.ino, + WriteHandleContextOperation::RecreateWriter { ref existing, .. } => existing.ino, } } } @@ -587,9 +637,11 @@ pub struct EncryptedFs { opened_files_for_read: RwLock>>, opened_files_for_write: RwLock>, // used for rw ops of actual serialization + // use std::sync::RwLock instead of tokio::sync::RwLock because we need to use it also in sync code in `DirectoryEntryIterator` and `DirectoryEntryPlusIterator` serialize_inode_locks: Arc>>>, // used for the update op serialize_update_inode_locks: Mutex>>, + // use std::sync::RwLock instead of tokio::sync::RwLock because we need to use it also in sync code in `DirectoryEntryIterator` and `DirectoryEntryPlusIterator` serialize_dir_entries_locks: Arc>>>, read_write_inode_locks: Mutex>>, key: ExpireValue, FsError, KeyProvider>, @@ -646,7 +698,7 @@ impl EncryptedFs { /// Create a new node in the filesystem pub async fn create_nod(&self, parent: u64, name: &SecretString, create_attr: CreateFileAttr, read: bool, write: bool) -> FsResult<(u64, FileAttr)> { if name.expose_secret() == "." || name.expose_secret() == ".." { - return Err(FsError::InvalidInput("name cannot be '.' or '..'".to_string())); + return Err(FsError::InvalidInput("name cannot be '.' or '..'")); } if !self.node_exists(parent) { return Err(FsError::InodeNotFound); @@ -681,12 +733,12 @@ impl EncryptedFs { ino: attr.ino, name: SecretString::from_str("$.").unwrap(), kind: FileType::Directory, - }, &*self.key.get().await?).await?; + }, &*self.key.get().await?, parent).await?; self.insert_directory_entry(attr.ino, DirectoryEntry { ino: parent, name: SecretString::from_str("$..").unwrap(), kind: FileType::Directory, - }, &*self.key.get().await?).await?; + }, &*self.key.get().await?, parent).await?; } } @@ -695,7 +747,7 @@ impl EncryptedFs { ino: attr.ino, name: SecretString::new(name.expose_secret().to_owned()), kind: attr.kind, - }, &*self.key.get().await?).await?; + }, &*self.key.get().await?, parent).await?; self.update_inode(parent, SetFileAttr::default() .with_mtime(SystemTime::now()) .with_ctime(SystemTime::now())).await?; @@ -734,9 +786,9 @@ impl EncryptedFs { SecretString::new(name.expose_secret().to_owned()) } }; - let name = crypto::encrypt_file_name(&name, &self.cipher, &*self.key.get().await?)?; + let name = crypto::encrypt_file_name_with_nonce_seed(&name, &self.cipher, &*self.key.get().await?, parent)?; let file = File::open(self.data_dir.join(CONTENTS_DIR).join(parent.to_string()).join(name))?; - let (inode, _): (u64, FileType) = bincode::deserialize_from(crypto::create_crypto_reader(file, &self.cipher, &*self.key.get().await?))?; + let (inode, _): (u64, FileType) = bincode::deserialize_from(self.create_crypto_reader(file, parent).await?)?; Ok(Some(self.get_inode(inode).await?)) } @@ -752,10 +804,10 @@ impl EncryptedFs { } if !self.exists_by_name(parent, name).await? { - return Err(FsError::NotFound("name not found".to_string())); + return Err(FsError::NotFound("name not found")); } - let attr = self.find_by_name(parent, name).await?.ok_or(FsError::NotFound("name not found".to_string()))?; + let attr = self.find_by_name(parent, name).await?.ok_or(FsError::NotFound("name not found"))?; if !matches!(attr.kind, FileType::Directory) { return Err(FsError::InvalidInodeType); } @@ -793,10 +845,10 @@ impl EncryptedFs { return Err(FsError::InvalidInodeType); } if !self.exists_by_name(parent, name).await? { - return Err(FsError::NotFound("name not found".to_string())); + return Err(FsError::NotFound("name not found")); } - let attr = self.find_by_name(parent, name).await?.ok_or(FsError::NotFound("name not found".to_string()))?; + let attr = self.find_by_name(parent, name).await?.ok_or(FsError::NotFound("name not found"))?; if !matches!(attr.kind, FileType::RegularFile) { return Err(FsError::InvalidInodeType); } @@ -833,7 +885,7 @@ impl EncryptedFs { SecretString::new(name.expose_secret().to_owned()) } }; - let name = crypto::encrypt_file_name(&name, &self.cipher, &*self.key.get().await?)?; + let name = crypto::encrypt_file_name_with_nonce_seed(&name, &self.cipher, &*self.key.get().await?, parent)?; Ok(self.data_dir.join(CONTENTS_DIR).join(parent.to_string()).join(name).exists()) } @@ -866,7 +918,7 @@ impl EncryptedFs { let path = self.data_dir.join(INODES_DIR).join(ino.to_string()); let file = OpenOptions::new().read(true).write(true).open(path).map_err(|_| { FsError::InodeNotFound })?; - Ok(bincode::deserialize_from::>, FileAttr>(Box::new(crypto::create_crypto_reader(file, &self.cipher, key)))?) + Ok(bincode::deserialize_from::>, FileAttr>(Box::new(crypto::create_reader(file, &self.cipher, key, ino)))?) } pub async fn get_inode(&self, ino: u64) -> FsResult { @@ -929,7 +981,7 @@ impl EncryptedFs { .create(true) .truncate(true) .open(&path)?; - let mut writer = crypto::create_crypto_writer(file, &self.cipher, key); + let mut writer = crypto::create_writer(file, &self.cipher, key, attr.ino); bincode::serialize_into(&mut writer, &attr)?; writer.flush()?; writer.finish()?; @@ -987,7 +1039,7 @@ impl EncryptedFs { // if we need an offset before the current position, we can't seek back, we need // to read from the beginning until the desired offset debug!("seeking back, recreating reader"); - self.do_with_read_handle(handle, ReadHandleContextOperation::RecreateDecryptor { existing: ctx }).await?; + self.do_with_read_handle(handle, ReadHandleContextOperation::RecreateReader { existing: ctx }).await?; ctx = guard.get(&handle).unwrap().lock().await; } let pos = ctx.pos; @@ -1056,11 +1108,12 @@ impl EncryptedFs { // if we have dirty content (ctx.pos > 0) and position is before file end we copy the rest of the file from position to the end if ctx.pos > 0 && ctx.pos < ctx.attr.size { debug!("dirty content, copying remaining of file"); - let ino_str = ctx.ino.to_string(); + let ino = ctx.ino; + let ino_str = ino.to_string(); let file_size = ctx.attr.size; let pos = ctx.pos; let len = file_size - pos; - crypto::copy_from_file_exact(&mut ctx.writer.as_mut().unwrap(), pos, len, &self.cipher, &*self.key.get().await?, self.data_dir.join(CONTENTS_DIR).join(ino_str))?; + crypto::copy_from_file_exact(&mut ctx.writer.as_mut().unwrap(), pos, len, &self.cipher, &*self.key.get().await?, ino, self.data_dir.join(CONTENTS_DIR).join(ino_str))?; ctx.pos += len; } @@ -1175,7 +1228,7 @@ impl EncryptedFs { let ino_str = ino.to_string(); let offset_in_bounds = offset.min(size); let len = offset_in_bounds - pos; - crypto::copy_from_file_exact(&mut ctx.writer.as_mut().unwrap(), pos, len, &self.cipher, &*self.key.get().await?, self.data_dir.join(CONTENTS_DIR).join(ino_str))?; + crypto::copy_from_file_exact(&mut ctx.writer.as_mut().unwrap(), pos, len, &self.cipher, &*self.key.get().await?, ino, self.data_dir.join(CONTENTS_DIR).join(ino_str))?; ctx.pos += len; } else { debug!("seeking backward or from the beginning of the file"); @@ -1191,7 +1244,7 @@ impl EncryptedFs { let guard = self.write_handles.read().await; let mut ctx = guard.get(&handle).unwrap().lock().await; let len = file_size - pos; - crypto::copy_from_file_exact(&mut ctx.writer.as_mut().unwrap(), pos, len, &self.cipher, &*self.key.get().await?, self.data_dir.join(CONTENTS_DIR).join(ino_str))?; + crypto::copy_from_file_exact(&mut ctx.writer.as_mut().unwrap(), pos, len, &self.cipher, &*self.key.get().await?, ino, self.data_dir.join(CONTENTS_DIR).join(ino_str))?; ctx.pos += len; } { @@ -1227,14 +1280,14 @@ impl EncryptedFs { let tmp_path = self.data_dir.join(CONTENTS_DIR).join(tmp_path_str); let tmp_file = OpenOptions::new().write(true).create(true).truncate(true).open(tmp_path.clone())?; - let writer = crypto::create_crypto_writer(tmp_file, &self.cipher, &*self.key.get().await?); + let writer = self.create_crypto_writer(tmp_file, ino).await?; debug!("recreating writer"); ctx.writer.replace(Box::new(writer)); ctx.pos = 0; ctx.path = tmp_path; let ino_str = ctx.ino.to_string(); let offset_in_bounds = offset.min(size); - crypto::copy_from_file_exact(&mut ctx.writer.as_mut().unwrap(), 0, offset_in_bounds, &self.cipher, &*self.key.get().await?, self.data_dir.join(CONTENTS_DIR).join(ino_str))?; + crypto::copy_from_file_exact(&mut ctx.writer.as_mut().unwrap(), 0, offset_in_bounds, &self.cipher, &*self.key.get().await?, ino, self.data_dir.join(CONTENTS_DIR).join(ino_str))?; ctx.pos += offset_in_bounds; } @@ -1302,7 +1355,7 @@ impl EncryptedFs { let len = self.write(dest_ino, dest_offset, &buf[copied..len], dest_fh).await?; if len == 0 && copied < size { error!(len, "Failed to copy all read bytes"); - return Err(FsError::Other("Failed to copy all read bytes".to_string())); + return Err(FsError::Other("Failed to copy all read bytes")); } copied += len; } @@ -1312,7 +1365,7 @@ impl EncryptedFs { /// Open a file. We can open multiple times for read but only one for write at a time. pub async fn open(&self, ino: u64, read: bool, write: bool) -> FsResult { if !read && !write { - return Err(FsError::InvalidInput("read and write cannot be false at the same time".to_string())); + return Err(FsError::InvalidInput("read and write cannot be false at the same time")); } if self.is_dir(ino) { return Err(FsError::InvalidInodeType); @@ -1370,13 +1423,12 @@ impl EncryptedFs { let in_path = self.data_dir.join(CONTENTS_DIR).join(attr.ino.to_string()); let in_file = OpenOptions::new().read(true).write(true).open(in_path.clone())?; - let key = self.key.get().await?; - let mut reader = crypto::create_crypto_reader(in_file, &self.cipher, &*key); + let mut reader = self.create_crypto_reader(in_file, ino).await?; let tmp_path_str = format!("{}.truncate.tmp", attr.ino.to_string()); let tmp_path = self.data_dir.join(CONTENTS_DIR).join(tmp_path_str); let tmp_file = OpenOptions::new().write(true).create(true).truncate(true).open(tmp_path.clone())?; - let mut writer = crypto::create_crypto_writer(tmp_file, &self.cipher, &*key); + let mut writer = self.create_crypto_writer(tmp_file, ino).await?; // copy existing data until new size debug!("copying data until new size"); @@ -1394,12 +1446,12 @@ impl EncryptedFs { let in_path = self.data_dir.join(CONTENTS_DIR).join(attr.ino.to_string()); let in_file = OpenOptions::new().read(true).write(true).open(in_path.clone())?; - let mut reader = crypto::create_crypto_reader(in_file, &self.cipher, &*self.key.get().await?); + let mut reader = self.create_crypto_reader(in_file, ino).await?; let tmp_path_str = format!("{}.truncate.tmp", attr.ino.to_string()); let tmp_path = self.data_dir.join(CONTENTS_DIR).join(tmp_path_str); let tmp_file = OpenOptions::new().write(true).create(true).truncate(true).open(tmp_path.clone())?; - let mut writer = crypto::create_crypto_writer(tmp_file, &self.cipher, &*self.key.get().await?); + let mut writer = self.create_crypto_writer(tmp_file, ino).await?; // copy existing data debug!("copying existing data"); @@ -1453,11 +1505,12 @@ impl EncryptedFs { // if we have dirty content (ctx.pos > 0) and position is before file end we copy the rest of the file from position to the end if ctx.pos > 0 && ctx.pos < ctx.attr.size { debug!("dirty content, copying remaining of file"); - let ino_str = ctx.ino.to_string(); + let ino = ctx.ino; + let ino_str = ino.to_string(); let file_size = ctx.attr.size; let pos = ctx.pos; let len = file_size - pos; - crypto::copy_from_file_exact(&mut ctx.writer.as_mut().unwrap(), pos, len, &self.cipher, &*self.key.get().await?, self.data_dir.join(CONTENTS_DIR).join(ino_str))?; + crypto::copy_from_file_exact(&mut ctx.writer.as_mut().unwrap(), pos, len, &self.cipher, &*self.key.get().await?, ino, self.data_dir.join(CONTENTS_DIR).join(ino_str))?; ctx.pos += len; } } @@ -1483,7 +1536,7 @@ impl EncryptedFs { { let guard = self.write_handles.read().await; let ctx = guard.get(&key).unwrap().lock().await; - self.do_with_write_handle(key, WriteHandleContextOperation::RecreateEncryptor { existing: ctx, reset_size: true }).await?; + self.do_with_write_handle(key, WriteHandleContextOperation::RecreateWriter { existing: ctx, reset_size: true }).await?; } } @@ -1504,7 +1557,7 @@ impl EncryptedFs { return Err(FsError::InvalidInodeType); } if !self.exists_by_name(parent, name).await? { - return Err(FsError::NotFound("name not found".to_string())); + return Err(FsError::NotFound("name not found")); } if parent == new_parent && name.expose_secret() == new_name.expose_secret() { @@ -1527,7 +1580,7 @@ impl EncryptedFs { ino: attr.ino, name: SecretString::new(new_name.expose_secret().to_owned()), kind: attr.kind, - }, &*self.key.get().await?).await?; + }, &*self.key.get().await?, new_parent).await?; let mut parent_attr = self.get_inode(parent).await?; parent_attr.mtime = SystemTime::now(); @@ -1545,25 +1598,20 @@ impl EncryptedFs { ino: new_parent, name: SecretString::from_str("$..").unwrap(), kind: FileType::Directory, - }, &*self.key.get().await?).await?; + }, &*self.key.get().await?, parent).await?; } Ok(()) } /// Create a crypto writer using internal encryption info. - pub async fn create_crypto_writer(&self, file: File) -> FsResult> { - Ok(crypto::create_crypto_writer(file, &self.cipher, &*self.key.get().await?)) + pub async fn create_crypto_writer(&self, file: File, nonce_seed: u64) -> FsResult> { + Ok(crypto::create_writer(file, &self.cipher, &*self.key.get().await?, nonce_seed)) } /// Create a crypto reader using internal encryption info. - pub async fn create_crypto_reader(&self, file: File) -> FsResult> { - Ok(crypto::create_crypto_reader(file, &self.cipher, &*self.key.get().await?)) - } - - /// Encrypts a string using internal encryption info. - pub async fn encrypt_string(&self, s: &SecretString) -> FsResult { - Ok(crypto::encrypt_string(s, &self.cipher, &*self.key.get().await?)?) + pub async fn create_crypto_reader(&self, file: File, nonce_seed: u64) -> FsResult> { + Ok(crypto::create_reader(file, &self.cipher, &*self.key.get().await?, nonce_seed)) } /// Decrypts a string using internal encryption info. @@ -1571,11 +1619,6 @@ impl EncryptedFs { Ok(crypto::decrypt_string(s, &self.cipher, &*self.key.get().await?)?) } - /// Normalize and encrypt a file name. - pub async fn normalize_end_encrypt_file_name(&self, name: &SecretString) -> FsResult { - Ok(crypto::encrypt_file_name(name, &self.cipher, &*self.key.get().await?)?) - } - /// Change the password of the filesystem used to access the encryption key. pub async fn change_password(data_dir: &str, old_password: SecretString, new_password: SecretString, cipher: Cipher) -> FsResult<()> { let data_dir = PathBuf::from(data_dir); @@ -1586,7 +1629,7 @@ impl EncryptedFs { let salt = crypto::hash_secret(&old_password); let initial_key = crypto::derive_key(&old_password, &cipher, salt)?; let enc_file = data_dir.join(SECURITY_DIR).join(KEY_ENC_FILENAME); - let reader = crypto::create_crypto_reader(File::open(enc_file.clone())?, &cipher, &initial_key); + let reader = crypto::create_reader(File::open(enc_file.clone())?, &cipher, &initial_key, 42_u64); let key_store: KeyStore = bincode::deserialize_from(reader).map_err(|_| FsError::InvalidPassword)?; // check hash if key_store.hash != crypto::hash(key_store.key.expose_secret()) { @@ -1597,8 +1640,8 @@ impl EncryptedFs { let salt = crypto::hash_secret(&new_password); let new_key = crypto::derive_key(&new_password, &cipher, salt)?; tokio::fs::remove_file(enc_file.clone()).await?; - let mut writer = crypto::create_crypto_writer(OpenOptions::new().read(true).write(true).create(true).truncate(true).open(enc_file.clone())?, - &cipher, &new_key); + let mut writer = crypto::create_writer(OpenOptions::new().read(true).write(true).create(true).truncate(true).open(enc_file.clone())?, + &cipher, &new_key, 42_u64); bincode::serialize_into(&mut writer, &key_store)?; writer.flush()?; writer.finish()?; @@ -1627,7 +1670,7 @@ impl EncryptedFs { { if let Some(ctx) = self.read_handles.read().await.get(&key) { debug!("recreating read handle"); - self.do_with_read_handle(key, ReadHandleContextOperation::RecreateDecryptor { existing: ctx.lock().await }).await?; + self.do_with_read_handle(key, ReadHandleContextOperation::RecreateReader { existing: ctx.lock().await }).await?; } } } @@ -1643,7 +1686,7 @@ impl EncryptedFs { continue; } debug!("recreating write handle"); - self.do_with_write_handle(key, WriteHandleContextOperation::RecreateEncryptor { existing: ctx.lock().await, reset_size: true }).await?; + self.do_with_write_handle(key, WriteHandleContextOperation::RecreateWriter { existing: ctx.lock().await, reset_size: true }).await?; } } @@ -1654,7 +1697,7 @@ impl EncryptedFs { let ino = op.get_ino(); let path = self.data_dir.join(CONTENTS_DIR).join(ino.to_string()); let file = OpenOptions::new().read(true).write(true).open(path)?; - let reader = crypto::create_crypto_reader(file, &self.cipher, &*self.key.get().await?); + let reader = self.create_crypto_reader(file, op.get_ino()).await?; let attr = self.get_inode_from_storage(ino, &*self.key.get().await?).await?; match op { ReadHandleContextOperation::Create { ino, lock } => { @@ -1669,7 +1712,7 @@ impl EncryptedFs { self.read_handles.write().await.insert(handle, Mutex::new(ctx)); self.opened_files_for_read.write().await.entry(ino).or_insert_with(|| HashSet::new()).insert(handle); } - ReadHandleContextOperation::RecreateDecryptor { mut existing } => { + ReadHandleContextOperation::RecreateReader { mut existing } => { existing.pos = 0; existing.reader.replace(Box::new(reader)); existing.attr.size = attr.size; @@ -1685,7 +1728,7 @@ impl EncryptedFs { .read(true) .write(true) .open(path.clone())?; - let writer = crypto::create_crypto_writer(file, &self.cipher, &*self.key.get().await?); + let writer = self.create_crypto_writer(file, op.get_ino()).await?; match op { WriteHandleContextOperation::Create { ino, lock } => { debug!("creating write handle"); @@ -1701,9 +1744,9 @@ impl EncryptedFs { self.write_handles.write().await.insert(handle, Mutex::new(ctx)); self.opened_files_for_write.write().await.insert(ino, handle); } - WriteHandleContextOperation::RecreateEncryptor { mut existing, reset_size } => { - existing.pos = 0; + WriteHandleContextOperation::RecreateWriter { mut existing, reset_size } => { debug!("recreating writer"); + existing.pos = 0; existing.writer.replace(Box::new(writer)); existing.path = path.clone(); if reset_size { @@ -1743,15 +1786,15 @@ impl EncryptedFs { ino: attr.ino, name: SecretString::from_str("$.").unwrap(), kind: FileType::Directory, - }, &*self.key.get().await?).await?; + }, &*self.key.get().await?, ROOT_INODE).await?; } Ok(()) } - async fn insert_directory_entry(&self, parent: u64, entry: DirectoryEntry, key: &SecretVec) -> FsResult<()> { + async fn insert_directory_entry(&self, parent: u64, entry: DirectoryEntry, key: &SecretVec, nonce_seed: u64) -> FsResult<()> { let parent_path = self.data_dir.join(CONTENTS_DIR).join(parent.to_string()); - let name = crypto::encrypt_file_name(&entry.name, &self.cipher, &*self.key.get().await?)?; + let name = crypto::encrypt_file_name_with_nonce_seed(&entry.name, &self.cipher, key, nonce_seed)?; let file_path = parent_path.join(name); let map = self.serialize_dir_entries_locks.write().unwrap(); @@ -1766,7 +1809,7 @@ impl EncryptedFs { // write inode and file type let entry = (entry.ino, entry.kind); - let mut writer = crypto::create_crypto_writer(file, &self.cipher, key); + let mut writer = crypto::create_writer(file, &self.cipher, key, nonce_seed); bincode::serialize_into(&mut writer, &entry)?; writer.flush()?; writer.finish()?; @@ -1776,7 +1819,7 @@ impl EncryptedFs { async fn remove_directory_entry(&self, parent: u64, name: &SecretString) -> FsResult<()> { let parent_path = self.data_dir.join(CONTENTS_DIR).join(parent.to_string()); - let name = crypto::encrypt_file_name(name, &self.cipher, &*self.key.get().await?)?; + let name = crypto::encrypt_file_name_with_nonce_seed(name, &self.cipher, &*self.key.get().await?, parent)?; let file_path = parent_path.join(name); let map = self.serialize_dir_entries_locks.write().unwrap(); @@ -1809,7 +1852,7 @@ impl EncryptedFs { if path.exists() { // read key - let reader = crypto::create_crypto_reader(File::open(path)?, cipher, &derived_key); + let reader = crypto::create_reader(File::open(path)?, cipher, &derived_key, 42_u64); let key_store: KeyStore = bincode::deserialize_from(reader).map_err(|_| FsError::InvalidPassword)?; // check hash if key_store.hash != crypto::hash(key_store.key.expose_secret()) { @@ -1828,8 +1871,8 @@ impl EncryptedFs { thread_rng().fill_bytes(&mut key); let key = SecretVec::new(key); let key_store = KeyStore::new(key); - let mut writer = crypto::create_crypto_writer(OpenOptions::new().read(true).write(true).create(true).open(path)?, - cipher, &derived_key); + let mut writer = crypto::create_writer(OpenOptions::new().read(true).write(true).create(true).open(path)?, + cipher, &derived_key, 42_u64); bincode::serialize_into(&mut writer, &key_store)?; writer.flush()?; writer.finish()?; diff --git a/src/encryptedfs/moved_test.rs b/src/encryptedfs/moved_test.rs index b3de59a1..4b264842 100644 --- a/src/encryptedfs/moved_test.rs +++ b/src/encryptedfs/moved_test.rs @@ -107,7 +107,7 @@ async fn copy_all_file_range(fs: &EncryptedFs, src_ino: u64, src_offset: u64, de } } -async fn read_exact(fs: &EncryptedFs, ino: u64, offset: u64, mut buf: &mut [u8], handle: u64) { +async fn read_exact(fs: &EncryptedFs, ino: u64, offset: u64, buf: &mut [u8], handle: u64) { let mut read = 0; while read < buf.len() { let len = fs.read(ino, offset + read as u64, &mut buf[read..], handle).await.unwrap(); diff --git a/src/encryptedfs_fuse3.rs b/src/encryptedfs_fuse3.rs index 747a8c0a..5b8661c9 100644 --- a/src/encryptedfs_fuse3.rs +++ b/src/encryptedfs_fuse3.rs @@ -36,7 +36,7 @@ const STATFS: ReplyStatFs = ReplyStatFs { const FMODE_EXEC: i32 = 0x20; -// const MAX_NAME_LENGTH: u32 = 255; +// const MAX_NAME_LENGTH: u32 = 255 - ENCRYPT_FILENAME_OVERHEAD_CHARS as u32; // Flags returned by the open request const FOPEN_DIRECT_IO: u32 = 1 << 0; // bypass page cache for this open file diff --git a/src/main.rs b/src/main.rs index 53059610..34409d70 100644 --- a/src/main.rs +++ b/src/main.rs @@ -110,7 +110,7 @@ fn get_cli_args() -> ArgMatches { .short('c') .value_name("cipher") .default_value("ChaCha20") - .help(format!("Encryption type, possible values: {}", + .help(format!("Cipher used for encryption, possible values: {}", Cipher::iter().fold(String::new(), |mut acc, x| { acc.push_str(format!("{acc}{}{x}", if acc.len() != 0 { ", " } else { "" }).as_str()); acc @@ -120,14 +120,14 @@ fn get_cli_args() -> ArgMatches { .arg( Arg::new("umount-on-start") .long("umount-on-start") - .short('x') + .short('u') .action(ArgAction::SetTrue) .help("If we should try to umount the mountpoint before starting the FUSE server. This can be useful when the previous run crashed or was forced kll and the mountpoint is still mounted."), ) .arg( Arg::new("auto_unmount") .long("auto_unmount") - .short('u') + .short('x') .default_value("true") .action(ArgAction::SetTrue) .help("Automatically unmount on process exit"),