pub mod core;
use tequel::hash::TequelHash;
use serde::{ Deserialize, Serialize };
use chrono::{Utc};
use std::{fs};
use std::path::PathBuf;
use std::io::{Error, ErrorKind};
use std::{fs::File, io::Write};
use memmap2::Mmap;
use crate::core::EmetError;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmetSeal {
pub original_hash: String,
pub timestamp: String,
pub tequel_version: String,
pub digital_signature: String
}
pub struct Emet {
tequel: TequelHash,
pub private_key: String
}
impl Emet {
pub fn up(private_key: String) -> Self {
let mut teqhash = TequelHash::new();
let secret = teqhash.tqlhash(private_key.as_bytes());
Self {
tequel: teqhash,
private_key: secret
}
}
pub fn seal(&mut self, path: &str) -> Result<EmetSeal, EmetError> {
let path = PathBuf::from(path);
if !path.exists() {
return Err(EmetError::IoError(Error::new(
ErrorKind::Other,
"Could not find original path to seal"
)))
}
let file = fs::File::open(&path).map_err(EmetError::IoError)?;
let mmap = unsafe { Mmap::map(&file).map_err(EmetError::IoError)? };
let file_hash = self.tequel.tqlhash(&mmap);
let timestamp = self.get_timestamp();
let mix = &format!("{}{}{}", file_hash, ×tamp, self.private_key);
let signature = self.tequel.tqlhash(mix.as_bytes());
Ok(EmetSeal {
original_hash: file_hash,
timestamp: timestamp,
tequel_version: "v1.2.0".to_string(),
digital_signature: signature
})
}
pub fn check(&mut self, path: &str, emet_path: &str) -> Result<(), EmetError> {
let original_path = PathBuf::from(path);
let emet_path = PathBuf::from(emet_path);
if !original_path.exists() {
return Err(EmetError::IoError(Error::new(
ErrorKind::Other,
"Could not find original path"
)))
}
if !emet_path.exists() {
return Err(EmetError::IoError(Error::new(
ErrorKind::Other,
"Could not find .emet path"
)))
}
let file_original = fs::File::open(&original_path).map_err(EmetError::IoError)?;
let mmap_orig = unsafe { Mmap::map(&file_original).map_err(EmetError::IoError)? };
let content_emet = fs::read_to_string(emet_path).map_err(|e| {
EmetError::IoError(e)
})?;
let emet_seal_stamped = serde_json::from_str::<EmetSeal>(&content_emet).map_err(|e| {
EmetError::IoError(e.into())
})?;
let orig_file_hash = self.tequel.tqlhash(&mmap_orig);
if orig_file_hash != emet_seal_stamped.original_hash {
return Err(EmetError::ForgedStamp);
}
let signature_emet = emet_seal_stamped.digital_signature;
let timestamp_emet = emet_seal_stamped.timestamp;
let mix = format!("{}{}{}", orig_file_hash, timestamp_emet, self.private_key);
let signature_test = self.tequel.tqlhash(&mix.as_bytes());
if signature_test != signature_emet {
return Err(EmetError::TruthViolated)
}
Ok(())
}
pub fn save_seal(&self, path: &str, seal: &EmetSeal) -> Result<(), Box<dyn std::error::Error>> {
let json = serde_json::to_string_pretty(seal).map_err(|e| {
e
})?;
let seal_path = format!("{}.emet", path);
let mut file = File::create(seal_path)?;
file.write_all(json.as_bytes()).map_err(|e| {
e
})?;
Ok(())
}
fn get_timestamp(&self) -> String {
let now = Utc::now();
now.to_rfc3339()
}
}