use std::path::{Path, PathBuf};
#[cfg(feature = "trust-manifest")]
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use crate::JaoResult;
#[derive(PartialEq)]
#[cfg_attr(feature = "trust-manifest", derive(Clone, Serialize, Deserialize))]
pub(crate) struct TrustedFileRecord {
pub(crate) fingerprint: String,
}
pub(crate) fn create_trust_record(path: impl AsRef<Path>) -> JaoResult<(PathBuf, TrustedFileRecord)> {
let canonical_path = std::fs::canonicalize(path)?;
let file_contents = std::fs::read(&canonical_path)?;
let mut hasher = Sha256::new();
hasher.update(
canonical_path
.to_string_lossy()
.as_bytes(),
);
hasher.update([0]);
hasher.update(file_contents);
Ok((
canonical_path,
TrustedFileRecord {
fingerprint: hex::encode(hasher.finalize()),
},
))
}
#[cfg(feature = "trust-manifest")]
pub(crate) mod manifest {
use core::fmt;
use std::collections::BTreeMap;
use std::path::Path;
use serde::{Deserialize, Serialize};
use crate::config::JaoConfig;
use crate::trust::{TrustedFileRecord, create_trust_record};
use crate::{JaoResult, storage};
#[derive(Clone, Copy, PartialEq, Eq)]
pub(crate) enum ScriptTrustState {
Trusted,
Unknown,
Modified,
}
impl fmt::Display for ScriptTrustState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let label = match self {
ScriptTrustState::Trusted => "trusted",
ScriptTrustState::Unknown => "unknown",
ScriptTrustState::Modified => "modified",
};
f.write_str(label)
}
}
#[derive(Default, Clone, Serialize, Deserialize)]
pub(crate) struct TrustedManifest {
#[serde(flatten)]
pub(crate) scripts: BTreeMap<String, TrustedFileRecord>,
}
pub(crate) fn load_or_init(config: &JaoConfig) -> JaoResult<TrustedManifest> {
let trust_manifest = match storage::load_from_storage(&config.trustfile)? {
Some(config) => config,
None => {
let default_trustfile = TrustedManifest::default();
storage::write_to_storage(&config.trustfile, &default_trustfile)?;
default_trustfile
}
};
Ok(trust_manifest)
}
pub(crate) fn determine_script_trust_state(script_path: impl AsRef<Path>, manifest: &TrustedManifest) -> JaoResult<ScriptTrustState> {
let (canonical_path, record) = create_trust_record(script_path.as_ref())?;
let key = canonical_path.to_string_lossy();
match manifest
.scripts
.get(key.as_ref())
{
None => Ok(ScriptTrustState::Unknown),
Some(entry) if *entry == record => Ok(ScriptTrustState::Trusted),
Some(_) => Ok(ScriptTrustState::Modified),
}
}
pub(crate) fn write_script_trust_record(
script_path: impl AsRef<Path>,
trustfile_path: impl AsRef<Path>,
manifest: &mut TrustedManifest,
) -> JaoResult<()> {
let (canonical_path, record) = create_trust_record(script_path.as_ref())?;
let key = canonical_path
.to_string_lossy()
.into_owned();
manifest
.scripts
.insert(key, record);
storage::write_to_storage(&trustfile_path, &manifest)
}
}