Newer
Older
use multi_key_manager::{file::open_rwlocked, LiveKeyMaterial, LiveKeys, WithNonce};
use std::{
fs::create_dir_all,
path::{Path, PathBuf},
sync::{Arc, Mutex, Weak},
Stefan Schindler
committed
thread::panicking,
use errors::{specify_io_error, specify_kme_error, CvfsErrors, CvfsResult};
use aead::{decrypt_file_buffered, encrypt_file_buffered};
use secure_file::ActiveFile;
pub use secure_file::SecureFileHandler;
const SUPERBLOCK_PATH: &str = "encfsstorage.superblock";
const BLOCK_PATH: &str = "blocks/";
const METADATA_PATH: &str = "metadata.index";
pub fn open_dir<P: AsRef<Path>>(
decryption_input: &str,
directory_path: P,
) -> Result<CryptoVFS, CvfsErrors> {
let directory_path = directory_path.as_ref().to_owned();
specify_io_error(create_dir_all(&directory_path), "unable to create_dir_all")?;
let keys_path = path_extend(&directory_path, SUPERBLOCK_PATH);
if keys_path.is_dir() {
return Err(CvfsErrors::GeneralIO(format!(
"keys_path must not be a directory: {:?}",
keys_path.display()
)));
}
let metadata_path = path_extend(&directory_path, METADATA_PATH);
if metadata_path.is_dir() {
return Err(CvfsErrors::GeneralIO(format!(
"metadata_path must not be a directory: {:?}",
metadata_path.display()
)));
}
let mut metadata_file = specify_io_error(open_rwlocked(&metadata_path), {
#[cfg(feature = "fs_locks")]
{
"unable to lock the metadata"
}
#[cfg(not(feature = "fs_locks"))]
{
"unable to open the metadata"
}
})?;
let blocks_path = path_extend(&directory_path, BLOCK_PATH);
specify_io_error(create_dir_all(blocks_path), "unable to create blocks_path")?;
let keys = if keys_path.is_file() {
LiveKeys::read_from_disk(decryption_input, keys_path)
} else {
LiveKeys::generate_new(decryption_input, keys_path)
};
let mut keys = specify_kme_error(keys, "unable to read or generate master key")?;
let content =
decrypt_file_buffered(&mut metadata_file, &mut keys.get_opening_master_key())?;
println!(" > CryptoVfsInner::open_dir");
Metadata::restore_from_slice(&content)?
};
Ok(CryptoVFS(Arc::new(Mutex::new(CryptoVfsInner {
metadata: metadata,
file_tag_counter: 0, // AtomicUsize::new(0),
}))))
}
/// A mounted virtual file system lock wrapper
#[derive(Debug)]
pub struct CryptoVFS(Arc<Mutex<CryptoVfsInner>>);
impl CryptoVFS {
pub fn exists(&self, path: &str) -> CvfsResult<bool> {
self.0.lock()?.exists(path)
}
/// Flush all open SecureFileHandler and the metadata to disk
///
/// Droping the VFS will automatically flush it
pub fn flush_all(&mut self) -> CvfsResult<()> {
self.0.lock()?.flush_all()
}
/// Opens a file in write-only mode.
///
/// This function will create a file if it does not exist, and will truncate it if it does.
pub fn create(&mut self, path: &str) -> CvfsResult<SecureFileHandler> {
let this = Arc::downgrade(&self.0);
self.0.lock()?.create(this, path)
}
/// Existing file handlers will return a `CvfsErrors::FileHandlerNotFound` on any subsequent action
///
/// Returns true if the File was removed with this action, false if it did not exist
pub fn delete(&mut self, path: &str) -> CvfsResult<bool> {
self.0.lock()?.delete(path)
}
}
/// A mounted virtual file system
Stefan Schindler
committed
#[derive(Debug)]
pub struct CryptoVfsInner {
/// Cyclic reference to itself
//this: Weak<Mutex<CryptoVfsInner>>,
metadata: Metadata,
file_tag_counter: u64, //AtomicUsize,
impl CryptoVfsInner {
/// Opens a file in write-only mode.
///
/// This function will create a file if it does not exist, and will truncate it if it does.
&mut self,
this: Weak<Mutex<CryptoVfsInner>>,
path: &str,
) -> CvfsResult<SecureFileHandler> {
//let weak_metadata = Arc::downgrade(&self.metadata);
//let mut metadata = self.metadata.lock()?;
//Arc::get_mut(&mut self.metadata).expect("CryptoVfsInner::create double get_mut(metadata)");
let Self {
metadata,
file_tag_counter,
..
} = self;
let entry: &mut FileInfo = metadata.files.entry(path.into()).or_insert_with(|| {
let mut real_path = path_extend(&self.real_base_path, BLOCK_PATH);
Stefan Schindler
committed
let mut real_block_name;
Stefan Schindler
committed
real_block_name = format!("{num:8x}.bin");
real_path.push(&real_block_name);
if real_path.exists() == false {
// this is very likely
break;
} else {
// remove the file name and try again
real_path.pop();
}
}
println!("new block path: {:?}", real_path.display());
Stefan Schindler
committed
FileInfo::new(real_block_name)
println!("CryptoVfsInner::create found entry: {entry:?}");
let id = match &entry.active {
Some(active) => {
// already open
active.id.clone()
}
None => {
// try to open the file
let id = FileTag::next(file_tag_counter);
let mut file_key = self
.keys
.get_opening_master_key_with_nonce(entry.get_opening_nonce());
// actually open the backing storage
Stefan Schindler
committed
let mut real_path = path_extend(&self.real_base_path, BLOCK_PATH);
real_path.push(&entry.real_block_name);
let active = ActiveFile::open_rwlocked(&real_path, id.clone(), &mut file_key)?;
println!("CryptoVfsInner::create made a file: {:?}", active);
entry.active = Some(active);
id
}
};
Ok(SecureFileHandler {
id,
cvfs: this, //Arc::downgrade(&self.metadata),
fn exists(&self, path: &str) -> CvfsResult<bool> {
Ok(self.metadata.files.contains_key(path))
Stefan Schindler
committed
/// Flush all open SecureFileHandler and the metadata to disk
///
/// Droping the VFS will automatically flush it
fn flush_all(&mut self) -> CvfsResult<()> {
Stefan Schindler
committed
self.keys.store_to_disk()?;
let metadata_path = path_extend(&self.real_base_path, METADATA_PATH);
let metadata_file = open_rwlocked(metadata_path)?;
//let mut metadata = self.metadata.lock()?;
self.metadata.store_to_disk(metadata_file, &mut self.keys)
fn delete(&mut self, path: &str) -> CvfsResult<bool> {
let found = if let Some(file_info) = self.metadata.files.remove(path) {
let mut block_path = path_extend(&self.real_base_path, BLOCK_PATH);
block_path.push(&file_info.real_block_name);
std::fs::remove_file(block_path)?;
true
} else {
false
};
Ok(found)
impl Drop for CryptoVfsInner {
Stefan Schindler
committed
fn drop(&mut self) {
if let Err(e) = self.flush_all() {
let error = format!("aead_vfs::CryptoVfsInner was not flushed to disk: {e:?}");
Stefan Schindler
committed
if panicking() {
eprintln!("{error}");
} else {
panic!("{error}");
}
}
}
}
pub fn path_extend<B: AsRef<Path>, E: AsRef<Path>>(base: B, ext: E) -> PathBuf {
let mut buf = base.as_ref().to_owned();
buf.push(ext);
buf
}
/// Generate a secure random value
pub fn random<T: ring::rand::RandomlyConstructable>() -> T {
let rng = ring::rand::SystemRandom::new();
ring::rand::generate(&rng)
.expect("unable to generate random value")
.expose()
}
pub fn random_u64() -> u64 {
let r = random::<[u8; 8]>();
u64::from_le_bytes(r)
}
#[cfg(test)]
mod tests {
use super::*;
/// ext must not start with a `/`
fn path<P: AsRef<Path>>(ext: P) -> PathBuf {
let mut p = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
// we are in a cargo workspace
p.pop();
p.push("target");
p.push("aead-vfs_tests");
std::fs::create_dir_all(&p).expect("unable to create testing directory");
p.push(ext);
p
}
#[test]
fn it_works() {
let result = open_dir("bla bla secure key bla", path("it_works"));
assert!(result.is_ok());
}
#[test]
fn encrypt_decrypt_cycle() {
let mut keys =
LiveKeys::generate_new("paper_key paper_key", path("encrypt_decrypt_cycle.keys"))
.unwrap();
let mut file = open_rwlocked(path("encrypt_decrypt_cycle.bin")).unwrap();
let random_data = b"abcdefghijklmnopqrstuvwxyz";
println!(" >> DBG: {}", random_data.len());
println!("nonce_before_encrypt: {:?}", keys.get_opening_nonce());
encrypt_file_buffered(&mut file, random_data, &mut keys.get_sealing_master_key())
.expect("unable to encrypt");
let nonce_after_encrypt = keys.get_opening_nonce();
println!("nonce_after_encrypt: {nonce_after_encrypt:?}");
let decrypted = decrypt_file_buffered(&mut file, &mut keys.get_opening_master_key())
.expect("unable to decrypt");
let nonce_after_decrypt = keys.get_opening_nonce();
println!("nonce_after_decrypt: {nonce_after_decrypt:?}");
assert_eq!(random_data, &*decrypted);
}
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
/*
#[test]
fn encrypt_decrypt_cycle_large_file() {
let mut keys = LiveKeys::generate_new(
"paper_key paper_key",
path("encrypt_decrypt_cycle_large_file.keys"),
)
.unwrap();
let mut file = open_rwlocked(path("encrypt_decrypt_cycle_large_file.bin")).unwrap();
let base_date = b"abcdefghijklmnopqrstuvwxyz";
let mut random_data = Vec::new();
for _ in 0..1024 * 1024 {
random_data.extend_from_slice(base_date);
}
println!(" >> DBG: {}", random_data.len());
println!("nonce_before_encrypt: {:?}", keys.get_opening_nonce());
encrypt_file_buffered(&mut file, &random_data, &mut keys).expect("unable to encrypt");
let nonce_after_encrypt = keys.get_opening_nonce();
println!("nonce_after_encrypt: {nonce_after_encrypt:?}");
let decrypted = decrypt_file_buffered(&mut file, &mut keys).expect("unable to decrypt");
let nonce_after_decrypt = keys.get_opening_nonce();
assert_eq!(random_data, decrypted);
}
// */
#[test]
fn double_store_and_decrypt_cycle() {
let mut keys = LiveKeys::generate_new(
"paper_key paper_key",
path("double_store_and_decrypt_cycle.keys"),
)
.unwrap();
let mut file = open_rwlocked(path("double_store_and_decrypt_cycle.bin")).unwrap();
let initial_data = b"alksdjflkasjdlfkjasldkfjlaksjdfl";
let random_data = b"abcdefghijklmnopqrstuvwxyz";
println!(" >> DBG: {}", random_data.len());
println!("1: {:?}", keys.get_opening_nonce());
let mut key = keys.get_sealing_master_key();
encrypt_file_buffered(&mut file, initial_data, &mut key).expect("unable to encrypt");
drop(key);
println!("2: {:?}", keys.get_opening_nonce());
encrypt_file_buffered(&mut file, random_data, &mut keys.get_sealing_master_key())
.expect("unable to encrypt");
let nonce_after_encrypt = keys.get_opening_nonce();
println!("3: {nonce_after_encrypt:?}");
let mut key = keys.get_opening_master_key();
let decrypted = decrypt_file_buffered(&mut file, &mut key);
drop(key);
let nonce_after_decrypt = keys.get_opening_nonce();
println!("4: {nonce_after_decrypt:?}");
let decrypted = decrypted.expect("unable to decrypt");
assert_eq!(random_data, &*decrypted);
}
#[test]
fn file_does_not_exist() {
Stefan Schindler
committed
let vfs = open_dir("bla bla secure key bla", path("file_does_not_exist"))
.expect("unable to open");
assert_eq!(Ok(false), vfs.exists("something"));
}
#[test]
fn file_does_exist() {
Stefan Schindler
committed
let mut vfs =
open_dir("bla bla secure key bla", path("file_does_exist")).expect("unable to open");
let _handler = vfs.create("something").unwrap();
assert_eq!(Ok(true), vfs.exists("something"));
Stefan Schindler
committed
println!("vfs: {:#?}", vfs);
let vfs = open_dir("bla bla secure key bla", path("file_does_exist")).unwrap();
assert_eq!(Ok(true), vfs.exists("something"));
#[test]
fn store_string() {
let clear = String::from("this is a string that I need to store privately");
let safe_name = "my thing.txt";
let repo_key = "this is a secure secre key for testing";
{
let mut vfs = open_dir(repo_key, path("store_string")).expect("unable to open");
let mut handler = vfs.create(safe_name).expect("unable to get handler");
handler
.write_all(clear.as_bytes())
.expect("unable to write_all");
handler.try_flush().unwrap();
//println!("{vfs:?}");
println!("\n222222222222222222\nsecond opening");
let mut vfs = open_dir(repo_key, path("store_string")).expect("unable to open");
let handler = vfs.create(safe_name).unwrap();
let decrypted = read_to_string(handler).unwrap();
assert_eq!(clear, decrypted);
println!("{vfs:?}");
#[test]
fn open_two() {
let mut vfs = open_dir("bla bla secure key bla", path("open_two")).expect("unable to open");
let mut a = vfs.create("a").unwrap();
let mut a1 = vfs.create("a1").unwrap();
let mut b = vfs.create("b").unwrap();
assert_ne!(a.id, b.id);
assert_ne!(a.id, a1.id);
a.set_len(0).unwrap();
a1.set_len(3).unwrap();
b.set_len(12).unwrap();
a.write_all(b"abcdefghijklmnopqrstuvwxyz").unwrap();
a1.write_all(b"abcdefghijklmnopqrstuvwxyz").unwrap();
b.write_all(b"01234566789012345678901234").unwrap();
}
#[test]
fn delete_a_file() {
let mut vfs =
open_dir("bla bla secure key bla", path("delete_a_file")).expect("unable to open");
let mut a = vfs.create("a").unwrap();
let mut b = vfs.create("b").unwrap();
b.set_len(12).unwrap();
a.write_all(b"abcdefghijklmnopqrstuvwxyz").unwrap();
b.write_all(b"abcdefghijklmnopqrstuvwxyz").unwrap();
vfs.flush_all().unwrap();
let mut b_block_path = path("delete_a_file");
b_block_path.push("blocks");
b_block_path.push(&vfs.0.lock().unwrap().metadata.files["b"].real_block_name);
assert!(b_block_path.exists());
assert_eq!(Ok(true), vfs.delete("b"));
assert!(b_block_path.exists() == false);
assert_eq!(Ok(false), vfs.exists("b"));
//assert!(false, "{vfs:#?}");
}