Skip to main content

emet/
lib.rs

1pub mod core;
2
3
4use tequel::hash::TequelHash;
5
6use serde::{ Deserialize, Serialize };
7use chrono::{Utc};
8
9use std::{fs};
10use std::path::PathBuf;
11use std::io::{Error, ErrorKind};
12use std::{fs::File, io::Write};
13use memmap2::Mmap;
14
15
16use crate::core::EmetError;
17
18
19/// Struct that represents the file sealed
20#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct EmetSeal {
22
23    /// Original Hash before be combined with timestamp and private key
24    pub original_hash: String,
25
26    /// Time stamp
27    pub timestamp: String,
28
29    /// Tequel's version used 
30    pub tequel_version: String,
31
32    /// Digital signature
33    pub digital_signature: String
34
35}
36
37
38
39pub struct Emet {
40    tequel: TequelHash,
41    pub private_key: String
42}
43
44
45impl Emet {
46
47    /// 'Up' executes the `TequelHash` and storage the private key
48    pub fn up(private_key: String) -> Self {
49
50        let mut teqhash = TequelHash::new();
51        let secret = teqhash.tqlhash(private_key.as_bytes());
52
53        Self {
54            tequel: teqhash,
55            private_key: secret
56        }
57        
58    }
59
60
61    /// Receive a file content (`&str`), turn to hash with `TequelHash` and combine with private key to create a Hash (signature). It returns a `EmetSeal`
62    pub fn seal(&mut self, path: &str) -> Result<EmetSeal, EmetError> {
63
64        let path = PathBuf::from(path);
65
66        if !path.exists() {
67            return Err(EmetError::IoError(Error::new(
68                ErrorKind::Other,
69                "Could not find original path to seal"
70            )))   
71        }
72
73
74        let file = fs::File::open(&path).map_err(EmetError::IoError)?;
75        
76        let mmap = unsafe { Mmap::map(&file).map_err(EmetError::IoError)? };
77
78        let file_hash = self.tequel.tqlhash(&mmap);
79        let timestamp = self.get_timestamp();
80        
81        let mix = &format!("{}{}{}", file_hash, &timestamp, self.private_key);
82        let signature = self.tequel.tqlhash(mix.as_bytes());
83
84        Ok(EmetSeal { 
85            original_hash: file_hash, 
86            timestamp: timestamp, 
87            tequel_version: "v1.2.0".to_string(), 
88            digital_signature: signature
89        })
90
91    }
92
93
94    /// Receive a original path and `.emet` file path to check if the hash match
95    pub fn check(&mut self, path: &str, emet_path: &str) -> Result<(), EmetError> {
96        
97        let original_path = PathBuf::from(path);
98        let emet_path = PathBuf::from(emet_path);
99
100        if !original_path.exists() {
101            return Err(EmetError::IoError(Error::new(
102                ErrorKind::Other,
103                "Could not find original path"
104            )))   
105        }
106
107        if !emet_path.exists() {
108            return Err(EmetError::IoError(Error::new(
109                ErrorKind::Other,
110                "Could not find .emet path"
111            )))   
112        }
113
114        // Reading the .emet file to get EmetSeal Struct
115        let file_original = fs::File::open(&original_path).map_err(EmetError::IoError)?;
116        let mmap_orig = unsafe { Mmap::map(&file_original).map_err(EmetError::IoError)? };
117    
118        let content_emet = fs::read_to_string(emet_path).map_err(|e| {
119            EmetError::IoError(e)
120        })?;
121        
122        let emet_seal_stamped = serde_json::from_str::<EmetSeal>(&content_emet).map_err(|e| {
123            EmetError::IoError(e.into())
124        })?;
125        
126
127        // Integrity
128
129        let orig_file_hash = self.tequel.tqlhash(&mmap_orig);
130
131        if orig_file_hash != emet_seal_stamped.original_hash {
132            return Err(EmetError::ForgedStamp);
133        }
134
135        // Signature Autenticity
136
137        let signature_emet = emet_seal_stamped.digital_signature;
138        let timestamp_emet = emet_seal_stamped.timestamp;
139
140        let mix = format!("{}{}{}", orig_file_hash, timestamp_emet, self.private_key);
141        let signature_test = self.tequel.tqlhash(&mix.as_bytes());
142
143        if signature_test != signature_emet {
144            return Err(EmetError::TruthViolated)
145        }
146
147        Ok(())
148
149    }
150
151
152
153    /// Receive a path and `EmetSeal` to save the `EmetSeal` in file `.emet`
154    pub fn save_seal(&self, path: &str, seal: &EmetSeal) -> Result<(), Box<dyn std::error::Error>> {
155
156
157
158        let json = serde_json::to_string_pretty(seal).map_err(|e| {
159            e
160        })?;
161
162        let seal_path = format!("{}.emet", path);
163
164        let mut file = File::create(seal_path)?;
165        
166        file.write_all(json.as_bytes()).map_err(|e| {
167            e
168        })?;
169
170        Ok(())
171
172    }
173
174
175
176
177
178
179    
180    fn get_timestamp(&self) -> String {
181        let now = Utc::now();
182        now.to_rfc3339()
183    }
184
185}