use crate::store::file_classification::KEYSET_FILENAME;
use crate::store::platform::fs::{write_file_atomically_with_fs, RealFs, StoreFs};
use crate::store::StoreError;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use zeroize::Zeroizing;
pub trait KeysetBackend: Send + Sync {
fn load(&self) -> Result<Option<Zeroizing<Vec<u8>>>, StoreError>;
fn persist(&self, encoded: &[u8]) -> Result<(), StoreError>;
}
pub struct FileKeysetBackend {
dir: PathBuf,
fs: Arc<dyn StoreFs>,
}
impl FileKeysetBackend {
#[must_use]
pub fn new(dir: impl Into<PathBuf>) -> Self {
Self::with_store_fs(dir, Arc::new(RealFs))
}
#[must_use]
pub fn with_store_fs(dir: impl Into<PathBuf>, fs: Arc<dyn StoreFs>) -> Self {
Self {
dir: dir.into(),
fs,
}
}
}
impl KeysetBackend for FileKeysetBackend {
fn load(&self) -> Result<Option<Zeroizing<Vec<u8>>>, StoreError> {
load_keyset_bytes(&self.dir, self.fs.as_ref())
}
fn persist(&self, encoded: &[u8]) -> Result<(), StoreError> {
persist_keyset_bytes(&self.dir, self.fs.as_ref(), encoded)
}
}
pub(crate) struct FileKeysetBackendRef<'a> {
pub(crate) dir: &'a Path,
pub(crate) fs: &'a dyn StoreFs,
}
impl KeysetBackend for FileKeysetBackendRef<'_> {
fn load(&self) -> Result<Option<Zeroizing<Vec<u8>>>, StoreError> {
load_keyset_bytes(self.dir, self.fs)
}
fn persist(&self, encoded: &[u8]) -> Result<(), StoreError> {
persist_keyset_bytes(self.dir, self.fs, encoded)
}
}
fn load_keyset_bytes(
dir: &Path,
fs: &dyn StoreFs,
) -> Result<Option<Zeroizing<Vec<u8>>>, StoreError> {
let path = dir.join(KEYSET_FILENAME);
fs.reject_symlink_leaf(&path, "crypto-shred-keyset")?;
match fs.read(&path) {
Ok(bytes) => Ok(Some(Zeroizing::new(bytes))),
Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(None),
Err(error) => Err(StoreError::Io(error)),
}
}
fn persist_keyset_bytes(dir: &Path, fs: &dyn StoreFs, encoded: &[u8]) -> Result<(), StoreError> {
let final_path = dir.join(KEYSET_FILENAME);
write_file_atomically_with_fs(
dir,
&final_path,
"crypto-shred-keyset",
|file| file.write_all(encoded).map_err(StoreError::Io),
fs,
)
}