#![deny(warnings)] //! [TSM.ID].[11031972] -- Platform X Ecosystem //! xcu-ouroboros -- Self-updating Binary Manager with OTA & Integrity use std::collections::HashMap; #[derive(Debug)] pub enum OuroborosError { VersionConflict(String), IntegrityFailed(String), RollbackFailed(String) } impl std::fmt::Display for OuroborosError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::VersionConflict(e) => write!(f, "Version: {e}"), Self::IntegrityFailed(e) => write!(f, "Integrity: {e}"), Self::RollbackFailed(e) => write!(f, "Rollback: {e}") } } } impl std::error::Error for OuroborosError {} #[derive(Debug, Clone)] pub struct BinaryVersion { pub version: String, pub hash: [u8; 32], pub size_bytes: u64, pub timestamp: u64, pub changelog: String } #[derive(Debug, Clone, PartialEq)] pub enum UpdateState { Idle, Downloading, Verifying, Swapping, Rollback, Complete, Failed } pub struct Ouroboros { current: BinaryVersion, history: Vec, state: UpdateState, max_rollback: usize, } impl Ouroboros { pub fn new(current: BinaryVersion, max_rollback: usize) -> Self { Self { current, history: Vec::new(), state: UpdateState::Idle, max_rollback } } /// Verify binary integrity using FNV hash pub fn verify_integrity(&self, binary_data: &[u8], expected_hash: &[u8; 32]) -> Result { let hash = Self::compute_hash(binary_data); if hash != *expected_hash { return Err(OuroborosError::IntegrityFailed( format!("Hash mismatch: computed {:02x}{:02x}..., expected {:02x}{:02x}...", hash[0], hash[1], expected_hash[0], expected_hash[1]))); } Ok(true) } fn compute_hash(data: &[u8]) -> [u8; 32] { let mut hash = [0u8; 32]; let mut state: u64 = 0xcbf29ce484222325; for (i, &b) in data.iter().enumerate() { state ^= b as u64; state = state.wrapping_mul(0x100000001b3); if i % 4 == 0 { hash[i % 32] ^= (state & 0xFF) as u8; } } for i in 0..32 { hash[i] ^= ((state >> (i % 8 * 8)) & 0xFF) as u8; } hash } /// Stage update: download → verify → swap pub fn stage_update(&mut self, new_version: BinaryVersion, binary_data: &[u8]) -> Result<(), OuroborosError> { self.state = UpdateState::Downloading; // Verify self.state = UpdateState::Verifying; self.verify_integrity(binary_data, &new_version.hash)?; // Compare versions if new_version.version == self.current.version { return Err(OuroborosError::VersionConflict(format!("Already at {}", self.current.version))); } // Swap self.state = UpdateState::Swapping; self.history.push(self.current.clone()); if self.history.len() > self.max_rollback { self.history.remove(0); } self.current = new_version; self.state = UpdateState::Complete; Ok(()) } /// Rollback to previous version pub fn rollback(&mut self) -> Result { self.state = UpdateState::Rollback; let prev = self.history.pop().ok_or_else(|| OuroborosError::RollbackFailed("No previous version".into()))?; self.current = prev.clone(); self.state = UpdateState::Complete; Ok(prev) } pub fn current_version(&self) -> &BinaryVersion { &self.current } pub fn state(&self) -> &UpdateState { &self.state } pub fn rollback_depth(&self) -> usize { self.history.len() } /// Version comparison (semver-like) pub fn is_newer(current: &str, candidate: &str) -> bool { let parse = |v: &str| -> Vec { v.split('.').filter_map(|s| s.parse().ok()).collect() }; let c = parse(current); let n = parse(candidate); for i in 0..c.len().max(n.len()) { let cv = c.get(i).copied().unwrap_or(0); let nv = n.get(i).copied().unwrap_or(0); if nv > cv { return true; } if nv < cv { return false; } } false } } #[cfg(test)] mod tests { use super::*; fn v1() -> BinaryVersion { BinaryVersion { version: "1.0.0".into(), hash: [0u8; 32], size_bytes: 1000, timestamp: 100, changelog: "init".into() } } #[test] fn test_version_compare() { assert!(Ouroboros::is_newer("1.0.0", "1.0.1")); assert!(Ouroboros::is_newer("1.0.0", "2.0.0")); assert!(!Ouroboros::is_newer("2.0.0", "1.0.0")); } #[test] fn test_integrity() { let o = Ouroboros::new(v1(), 3); let data = b"test binary"; let hash = Ouroboros::compute_hash(data); assert!(o.verify_integrity(data, &hash).is_ok()); let bad_hash = [0xFF; 32]; assert!(o.verify_integrity(data, &bad_hash).is_err()); } #[test] fn test_rollback() { let data = b"new binary"; let hash = Ouroboros::compute_hash(data); let mut o = Ouroboros::new(v1(), 3); let v2 = BinaryVersion { version: "2.0.0".into(), hash, size_bytes: 500, timestamp: 200, changelog: "v2".into() }; o.stage_update(v2, data).unwrap(); assert_eq!(o.current_version().version, "2.0.0"); let prev = o.rollback().unwrap(); assert_eq!(prev.version, "1.0.0"); } }