security_cam_common/
encryption.rs1use std::io::ErrorKind;
2use actix_web::web::{Bytes};
3use aes_gcm::aead::stream;
4use aes_gcm::aead::{Key, KeyInit};
5
6use aes_gcm::Aes256Gcm;
7use argon2::Argon2;
8use argon2::password_hash::rand_core::OsRng;
9use argon2::password_hash::SaltString;
10use async_stream::stream;
11use base64::{Engine, engine};
12use futures_core::Stream;
13use shuttle_runtime::tokio;
14use shuttle_runtime::tokio::io::AsyncReadExt;
15
16
17const BUFFER_LEN: usize = 500;
18const NONCE_LEN: usize = 16;
19trait SizedError: std::error::Error + std::marker::Sized {}
20
21pub fn encrypt_stream(key: Key<Aes256Gcm>, salt: SaltString, mut file: tokio::fs::File,) -> impl Stream<Item=Result<Vec<u8> , std::io::Error>> {
22 let s = stream! {
23 let b64_decoder = base64::engine::general_purpose::STANDARD_NO_PAD;
24 let nonce = b64_decoder.decode(salt.as_str()).map_err(|e| {
25 std::io::Error::new(ErrorKind::Other, e.to_string())
26 })?;
27
28 let mut buffer = [0u8; BUFFER_LEN];
29
30 let mut encryptor = stream::EncryptorBE32::from_aead(
31 Aes256Gcm::new(&key),
32 nonce[0..7].into()
33 );
34 let mut last_chunk = Vec::new(); yield Ok(nonce); loop {
37 let read_count = file.read(&mut buffer).await?;
38 if read_count == BUFFER_LEN {
39 let encrypted = encryptor.encrypt_next(&buffer[..])
40 .map_err(|e| std::io::Error::new(ErrorKind::Other, e.to_string()));
41 yield encrypted;
42 } else if read_count == 0 {
43 break;
44 } else {
45 last_chunk = buffer[..read_count].to_vec();
46 }
47 }
48 let encrypted = (encryptor)
49 .encrypt_last(&last_chunk[..])
50 .map_err(|e| std::io::Error::new(ErrorKind::Other, e.to_string()));
51 yield encrypted;
52 };
53 s }
55
56
57pub fn decrypt_stream(mut file: tokio::fs::File, password: String) -> impl Stream<Item=Result<Bytes, Box<dyn std::error::Error + 'static>>> {
58 let s = stream! {
59 let mut buffer = [0u8; BUFFER_LEN+16];
61 let mut salt = [0u8; NONCE_LEN];
62 file.read_exact(&mut salt).await?;
63 let salt = salt.to_vec();
64
65 let key = generate_keystream(&password,&salt)?;
67 let mut decryptor = stream::DecryptorBE32::from_aead(
68 Aes256Gcm::new(&key),
69 (&salt[0..7]).into()
70 );
71
72 let mut last_chunk = Vec::new();
74 loop {
75 let read_count = file.read(&mut buffer).await?;
76 if read_count == BUFFER_LEN+16 {
77 let decrypted: Result<Vec<u8>, Box<dyn std::error::Error>> = decryptor.decrypt_next(&buffer[..]).map_err(|e|
78 {
79 println!("got an error: {:?}",e);
80 e.to_string().into()
81 });
82 yield Ok(Bytes::from(decrypted?));
83 }else if read_count == 0 {
84 println!("no bytes read");
85 break;
86 } else {
87 last_chunk = buffer[..read_count].to_vec();
88 }
89 }
90 let decrypted: Result<Vec<u8>, Box<dyn std::error::Error>> = (decryptor)
91 .decrypt_last(&last_chunk[..])
92 .map_err(|e| e.to_string().into());
93 yield Ok(Bytes::from(decrypted?));
94 };
95 s
96}
97
98
99pub fn generate_key(password: &str) -> Result<(Key<Aes256Gcm>,SaltString), Box<dyn std::error::Error>> {
101 let mut key_out = [0u8; 32];
102 let salt = SaltString::generate(&mut OsRng);
103 let b64_decoder = engine::general_purpose::STANDARD_NO_PAD;
104 let salt_bytes = b64_decoder.decode(salt.as_str())?;
105 Argon2::default().hash_password_into(password.as_bytes(), &salt_bytes, &mut key_out).map_err(|e|e.to_string())?;
106 Ok((key_out.into(),salt ))
107}
108
109pub fn generate_keystream(password:&str,salt: &[u8]) -> Result<Key<Aes256Gcm>, Box<dyn std::error::Error>> {
111 let mut key_out = [0u8; 32];
112 Argon2::default().hash_password_into(password.as_bytes(), salt, &mut key_out).map_err(|e|e.to_string())?;
113 Ok(key_out.into())
114}
115
116
117#[cfg(test)]
132mod tests {
133 use super::*;
134 use tokio::io::AsyncWriteExt;
135 use tokio_stream::StreamExt;
136
137
138 use std::fs::remove_file;
139
140
141 #[actix_web::test]
142 async fn test_encrypt_decrypt() -> Result<(), Box<dyn std::error::Error>> {
143 let password = "test_password";
144 let (key, salt) = generate_key(password)?;
145
146 let test_data = "This is some test data";
147 let tmp_file_path = "/var/tmp/test_file.txt";
148 let mut tmp_file = tokio::fs::File::create(tmp_file_path).await?;
149 tmp_file.write_all(test_data.as_bytes()).await?;
150
151 let file = tokio::fs::File::open(tmp_file_path).await?;
152 let mut encrypted_stream = Box::pin(encrypt_stream(key, salt, file));
154
155 let tmp_file_path2 = "/var/tmp/test_file2.txt";
156 let mut tmp_file2 = tokio::fs::File::create(tmp_file_path2).await?;
157 while let Some(chunk) = encrypted_stream.next().await {
158 tmp_file2.write_all(&chunk.unwrap()).await?;
159 }
160
161 let file2 = tokio::fs::File::open(tmp_file_path2).await?;
162 let mut decrypted_stream = Box::pin(decrypt_stream(file2, "test_password".to_owned()));
164
165 let mut decrypted_data = Vec::new();
166 while let Some(chunk) = decrypted_stream.next().await {
167 let chunk = chunk?;
168 decrypted_data.extend_from_slice(&chunk);
169 }
170
171 assert_eq!(test_data.as_bytes(), decrypted_data.as_slice());
172
173 remove_file(tmp_file_path)?;
174 remove_file(tmp_file_path2)?;
175
176 Ok(())
177 }
178
179}
180
181