//! Domain-separated SHA-256 identity of the exact file-backed bytes installed //! in a narrow ELF user program. This is an identity witness, not a signature. use sha2::{Digest, Sha256}; pub const DOMAIN: &[u8] = b"TurkOS/ELF-PROGRAM/v1\0"; pub fn digest(entry: u64, code: &[u8], data: &[u8]) -> [u8; 32] { let mut sha = Sha256::new(); sha.update(DOMAIN); sha.update(entry.to_le_bytes()); sha.update((code.len() as u64).to_le_bytes()); sha.update(code); sha.update((data.len() as u64).to_le_bytes()); sha.update(data); sha.finalize().into() } pub fn hex_lower(bytes: &[u8; 32]) -> [u8; 64] { let mut output = [0u8; 64]; const HEX: &[u8; 16] = b"0123456789abcdef"; for (index, byte) in bytes.iter().enumerate() { output[2 * index] = HEX[(byte >> 4) as usize]; output[2 * index + 1] = HEX[(byte & 15) as usize]; } output } #[cfg(test)] mod tests { use super::*; #[test] fn sha256_library_matches_published_hello_world_vector() { let digest = Sha256::digest(b"hello world"); let expected = b"b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"; assert_eq!(hex_lower(&digest.into()), *expected); } #[test] fn program_identity_binds_entry_lengths_and_file_backed_bytes() { let baseline = digest(0x8000_0000, b"CODE", b"HELLO"); assert_ne!(baseline, digest(0x8000_0004, b"CODE", b"HELLO")); assert_ne!(baseline, digest(0x8000_0000, b"CODF", b"HELLO")); assert_ne!(baseline, digest(0x8000_0000, b"CODE", b"WORLD")); assert_ne!(baseline, digest(0x8000_0000, b"CODE\0", b"HELLO")); assert_eq!(hex_lower(&baseline).len(), 64); } }