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#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct EmetSeal {
22
23 pub original_hash: String,
25
26 pub timestamp: String,
28
29 pub tequel_version: String,
31
32 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 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 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, ×tamp, 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 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 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 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 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 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}