feat(security): implement Phase 2 Modell A (Steganografischer Alibi-Carrier für Plausible Deniability)
This commit is contained in:
+108
-8
@@ -16,6 +16,7 @@ use futures_util::stream;
|
||||
use tracing::{debug, error, warn};
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
use crate::carrier::CarrierFs;
|
||||
use crate::crypto::{decrypt_chunk, encrypt_chunk, CHUNK_SIZE};
|
||||
use crate::storage::{Database, NodeRecord};
|
||||
|
||||
@@ -386,6 +387,10 @@ impl DavFile for SanctumFile {
|
||||
pub struct SanctumFs {
|
||||
db: Database,
|
||||
dek: Arc<Zeroizing<[u8; 32]>>,
|
||||
#[allow(dead_code)]
|
||||
carrier_dek: Option<Arc<Zeroizing<[u8; 32]>>>,
|
||||
carrier_node_id: Option<i64>,
|
||||
carrier_fs: Option<CarrierFs>,
|
||||
format_version: u32,
|
||||
anti_leak: bool,
|
||||
vault_id: u32,
|
||||
@@ -412,14 +417,55 @@ impl SanctumFs {
|
||||
format_version: u32,
|
||||
anti_leak: bool,
|
||||
vault_id: u32,
|
||||
) -> Self {
|
||||
Self::with_carrier(db, dek, None, None, format_version, anti_leak, vault_id)
|
||||
}
|
||||
|
||||
pub fn with_carrier(
|
||||
db: Database,
|
||||
dek: Zeroizing<[u8; 32]>,
|
||||
carrier_dek: Option<Zeroizing<[u8; 32]>>,
|
||||
carrier_node_id: Option<i64>,
|
||||
format_version: u32,
|
||||
anti_leak: bool,
|
||||
vault_id: u32,
|
||||
) -> Self {
|
||||
let now = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.map(|d| d.as_secs())
|
||||
.unwrap_or(0);
|
||||
let dek_arc = Arc::new(dek);
|
||||
let carrier_dek_arc = carrier_dek.map(Arc::new);
|
||||
|
||||
let carrier_fs = if vault_id == 1 {
|
||||
if let (Some(ref c_dek), Some(c_nid)) = (&carrier_dek_arc, carrier_node_id) {
|
||||
match CarrierFs::load(
|
||||
db.clone(),
|
||||
c_nid,
|
||||
c_dek.clone(),
|
||||
dek_arc.clone(),
|
||||
format_version,
|
||||
anti_leak,
|
||||
) {
|
||||
Ok(cfs) => Some(cfs),
|
||||
Err(e) => {
|
||||
warn!("CarrierFs konnte nicht initialisiert werden: {e}");
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Self {
|
||||
db,
|
||||
dek: Arc::new(dek),
|
||||
dek: dek_arc,
|
||||
carrier_dek: carrier_dek_arc,
|
||||
carrier_node_id,
|
||||
carrier_fs,
|
||||
format_version,
|
||||
anti_leak,
|
||||
vault_id,
|
||||
@@ -432,7 +478,11 @@ impl SanctumFs {
|
||||
}
|
||||
|
||||
pub fn last_activity(&self) -> Arc<AtomicU64> {
|
||||
self.last_activity.clone()
|
||||
if let Some(ref cfs) = self.carrier_fs {
|
||||
cfs.last_activity()
|
||||
} else {
|
||||
self.last_activity.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_anti_leak_enabled(&self) -> bool {
|
||||
@@ -440,11 +490,15 @@ impl SanctumFs {
|
||||
}
|
||||
|
||||
pub fn touch(&self) {
|
||||
let now = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.map(|d| d.as_secs())
|
||||
.unwrap_or(0);
|
||||
self.last_activity.store(now, Ordering::Relaxed);
|
||||
if let Some(ref cfs) = self.carrier_fs {
|
||||
cfs.touch();
|
||||
} else {
|
||||
let now = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.map(|d| d.as_secs())
|
||||
.unwrap_or(0);
|
||||
self.last_activity.store(now, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
fn path_to_str(path: &DavPath) -> String {
|
||||
@@ -493,6 +547,10 @@ impl DavFileSystem for SanctumFs {
|
||||
path: &'a DavPath,
|
||||
options: OpenOptions,
|
||||
) -> FsFuture<'a, Box<dyn DavFile>> {
|
||||
if let Some(ref cfs) = self.carrier_fs {
|
||||
return cfs.open(path, options);
|
||||
}
|
||||
|
||||
Box::pin(async move {
|
||||
let path_str = Self::path_to_str(path);
|
||||
let (parent_path, file_name) = self.split_parent_and_name(&path_str);
|
||||
@@ -520,6 +578,11 @@ impl DavFileSystem for SanctumFs {
|
||||
|
||||
let node = match existing_node {
|
||||
Some(n) => {
|
||||
// Schutz der Trägerdatei im Decoy Vault: Keine Schreib- oder Truncate-Operationen erlaubt!
|
||||
if self.carrier_node_id == Some(n.id) && (options.write || options.truncate || options.append) {
|
||||
return Err(FsError::Forbidden);
|
||||
}
|
||||
|
||||
if n.is_dir && (options.write || options.append) {
|
||||
return Err(FsError::Forbidden);
|
||||
}
|
||||
@@ -579,8 +642,12 @@ impl DavFileSystem for SanctumFs {
|
||||
fn read_dir<'a>(
|
||||
&'a self,
|
||||
path: &'a DavPath,
|
||||
_meta: ReadDirMeta,
|
||||
meta: ReadDirMeta,
|
||||
) -> FsFuture<'a, FsStream<Box<dyn DavDirEntry>>> {
|
||||
if let Some(ref cfs) = self.carrier_fs {
|
||||
return cfs.read_dir(path, meta);
|
||||
}
|
||||
|
||||
Box::pin(async move {
|
||||
self.touch();
|
||||
let path_str = Self::path_to_str(path);
|
||||
@@ -615,6 +682,10 @@ impl DavFileSystem for SanctumFs {
|
||||
}
|
||||
|
||||
fn metadata<'a>(&'a self, path: &'a DavPath) -> FsFuture<'a, Box<dyn DavMetaData>> {
|
||||
if let Some(ref cfs) = self.carrier_fs {
|
||||
return cfs.metadata(path);
|
||||
}
|
||||
|
||||
Box::pin(async move {
|
||||
let path_str = Self::path_to_str(path);
|
||||
if path_str != "/" && !path_str.is_empty() {
|
||||
@@ -636,10 +707,17 @@ impl DavFileSystem for SanctumFs {
|
||||
}
|
||||
|
||||
fn symlink_metadata<'a>(&'a self, path: &'a DavPath) -> FsFuture<'a, Box<dyn DavMetaData>> {
|
||||
if let Some(ref cfs) = self.carrier_fs {
|
||||
return cfs.symlink_metadata(path);
|
||||
}
|
||||
self.metadata(path)
|
||||
}
|
||||
|
||||
fn create_dir<'a>(&'a self, path: &'a DavPath) -> FsFuture<'a, ()> {
|
||||
if let Some(ref cfs) = self.carrier_fs {
|
||||
return cfs.create_dir(path);
|
||||
}
|
||||
|
||||
Box::pin(async move {
|
||||
self.touch();
|
||||
let path_str = Self::path_to_str(path);
|
||||
@@ -668,6 +746,10 @@ impl DavFileSystem for SanctumFs {
|
||||
}
|
||||
|
||||
fn remove_dir<'a>(&'a self, path: &'a DavPath) -> FsFuture<'a, ()> {
|
||||
if let Some(ref cfs) = self.carrier_fs {
|
||||
return cfs.remove_dir(path);
|
||||
}
|
||||
|
||||
Box::pin(async move {
|
||||
self.touch();
|
||||
let path_str = Self::path_to_str(path);
|
||||
@@ -693,6 +775,10 @@ impl DavFileSystem for SanctumFs {
|
||||
}
|
||||
|
||||
fn remove_file<'a>(&'a self, path: &'a DavPath) -> FsFuture<'a, ()> {
|
||||
if let Some(ref cfs) = self.carrier_fs {
|
||||
return cfs.remove_file(path);
|
||||
}
|
||||
|
||||
Box::pin(async move {
|
||||
self.touch();
|
||||
let path_str = Self::path_to_str(path);
|
||||
@@ -704,6 +790,11 @@ impl DavFileSystem for SanctumFs {
|
||||
return Err(FsError::Forbidden);
|
||||
}
|
||||
|
||||
// Schutz der Trägerdatei im Decoy Vault: Löschen verboten!
|
||||
if self.carrier_node_id == Some(node.id) {
|
||||
return Err(FsError::Forbidden);
|
||||
}
|
||||
|
||||
self.db
|
||||
.delete_node(node.id)
|
||||
.map_err(|_| FsError::GeneralFailure)?;
|
||||
@@ -717,6 +808,10 @@ impl DavFileSystem for SanctumFs {
|
||||
from: &'a DavPath,
|
||||
to: &'a DavPath,
|
||||
) -> FsFuture<'a, ()> {
|
||||
if let Some(ref cfs) = self.carrier_fs {
|
||||
return cfs.rename(from, to);
|
||||
}
|
||||
|
||||
Box::pin(async move {
|
||||
self.touch();
|
||||
let from_str = Self::path_to_str(from);
|
||||
@@ -726,6 +821,11 @@ impl DavFileSystem for SanctumFs {
|
||||
.resolve_path(&from_str)?
|
||||
.ok_or(FsError::NotFound)?;
|
||||
|
||||
// Schutz der Trägerdatei im Decoy Vault: Umbenennen verboten!
|
||||
if self.carrier_node_id == Some(node.id) {
|
||||
return Err(FsError::Forbidden);
|
||||
}
|
||||
|
||||
let (to_parent_path, to_name) = self.split_parent_and_name(&to_str);
|
||||
|
||||
if self.anti_leak && is_leak_file(to_name) {
|
||||
|
||||
Reference in New Issue
Block a user