nook-vault 0.11.0

Amorphous end-to-end encrypted push/pull file vault with client and server binaries.
Documentation
use crate::{NookError, Result};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum ObjectType {
    Manifest,
    Content,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum NodeType {
    #[serde(rename = "file")]
    File,
    #[serde(rename = "directory")]
    Directory,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Node {
    pub node_id: u64,
    pub parent_id: Option<u64>,
    pub name: String,
    pub node_type: NodeType,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub content_object_id: Option<[u8; 32]>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub wrapped_dek: Option<Vec<u8>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub logical_size: Option<u64>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Manifest {
    pub manifest_version: u32,
    pub root_node_id: u64,
    pub nodes: Vec<Node>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub previous_manifest_hash: Option<String>,
    pub integrity_checksum: String,
}

#[derive(Serialize)]
struct ManifestIntegrityView<'a> {
    manifest_version: u32,
    root_node_id: u64,
    nodes: &'a [Node],
    #[serde(skip_serializing_if = "Option::is_none")]
    previous_manifest_hash: &'a Option<String>,
}

impl Manifest {
    pub fn compute_integrity(&self) -> Result<String> {
        let view = ManifestIntegrityView {
            manifest_version: self.manifest_version,
            root_node_id: self.root_node_id,
            nodes: &self.nodes,
            previous_manifest_hash: &self.previous_manifest_hash,
        };
        let bytes =
            serde_json::to_vec(&view).map_err(|e| NookError::Serialization(e.to_string()))?;
        Ok(hex::encode(Sha256::digest(bytes)))
    }

    pub fn validate_integrity(&self) -> Result<()> {
        let expected = self.compute_integrity()?;
        if expected != self.integrity_checksum {
            return Err(NookError::Integrity("manifest checksum mismatch".into()));
        }
        Ok(())
    }
}