encryptable_tokio_fs/tokio/
encryptable_fs.rs1use std::fs::Metadata;
2use std::io::ErrorKind;
3use crate::crypto::cryptor::KEY_LEN;
4use crate::crypto::cryptor::{CryptorAsyncReader, CryptorAsyncWriter};
5use std::path::Path;
6use std::pin::Pin;
7use std::task::{Context, Poll};
8use sha2::{Digest, Sha256};
9use tokio::io;
10pub use tokio::fs::*;
11use tokio::io::{AsyncReadExt, AsyncWriteExt};
12
13pub type ChaCha20Key = [u8; KEY_LEN];
14
15pub struct EncryptionKeys {
16 content_key: ChaCha20Key,
17 _path_key: ChaCha20Key,
18}
19
20static mut ENCRYPTION_KEYS: Option<EncryptionKeys> = None;
21
22pub fn set_keys(keys: EncryptionKeys) -> Option<EncryptionKeys> {
23 let old_keys = unsafe {
24 #[allow(static_mut_refs)]
25 ENCRYPTION_KEYS.replace(keys)
26 };
27 std::sync::atomic::fence(std::sync::atomic::Ordering::SeqCst);
28 old_keys
29
30}
31
32pub fn set_keys_from_passphrase(passphrase: &str) {
33
34 assert!(passphrase.len() >= KEY_LEN, "`passphrase` needs to be no less than {KEY_LEN} bytes");
35
36 let key = derive_key(passphrase.as_bytes());
37 let derived_key = derive_key(key.as_ref());
38 set_keys(EncryptionKeys {
39 content_key: key,
40 _path_key: derived_key,
41 });
42}
43
44pub fn set_keys_from_base(key: ChaCha20Key) {
45 let derived_key = derive_key(key.as_ref());
46 set_keys(EncryptionKeys {
47 content_key: key,
48 _path_key: derived_key,
49 });
50}
51
52fn get_content_key() -> Option<&'static ChaCha20Key> {
53 unsafe {
54 #[allow(static_mut_refs)]
55 ENCRYPTION_KEYS.as_ref().map(|keys| &keys.content_key)
56 }
57}
58
59fn derive_key(key: &[u8]) -> ChaCha20Key {
60 let mut hasher = Sha256::new();
61 hasher.update(key);
62 let hash_result = hasher.finalize();
63 hash_result.into()
64}
65
66pub async fn read(path: impl AsRef<Path>) -> io::Result<Vec<u8>> {
71 match get_content_key() {
72 None => tokio::fs::read(path).await,
73 Some(_key) => {
74 let file = File::open(path).await?;
75 let size = file.metadata().await.map(|m| m.len() as usize).ok();
76 let mut contents = Vec::with_capacity(size.unwrap_or(0));
77 let mut reader = io::BufReader::new(file);
78 reader.read_to_end(&mut contents).await?;
79 Ok(contents)
80 },
81 }
82}
83
84pub async fn read_to_string(path: impl AsRef<Path>) -> std::io::Result<String> {
86 match get_content_key() {
87 None => tokio::fs::read_to_string(path).await,
88 Some(_key) => {
89 let file = File::open(path).await?;
90 let size = file.metadata().await.map(|m| m.len() as usize).ok();
91 let mut string = String::with_capacity(size.unwrap_or(0));
92 let mut contents = unsafe { string.as_mut_vec() }; let mut reader = io::BufReader::new(file);
94 reader.read_to_end(&mut contents).await?;
95 Ok(string)
96 },
97 }
98}
99
100pub async fn write(path: impl AsRef<Path>, contents: impl AsRef<[u8]>) -> io::Result<()> {
102 match get_content_key() {
103 None => tokio::fs::write(path, contents).await,
104 Some(_key) => {
105 let file = File::create(path).await?;
106 let mut writer = io::BufWriter::new(file);
107 writer.write_all(contents.as_ref()).await?;
108 writer.shutdown().await?;
109 Ok(())
110 },
111 }
112}
113
114pub enum File {
116 Plain { tokio_file: tokio::fs::File },
117 EncryptedReader { cryptor_async_reader: CryptorAsyncReader<'static, tokio::fs::File> },
118 EncryptedWriter { cryptor_async_writer: CryptorAsyncWriter<tokio::fs::File> },
119}
120
121impl File {
122
123 pub async fn open(path: impl AsRef<Path>) -> io::Result<File> {
125 Ok(
126 match get_content_key() {
127 None => File::Plain { tokio_file: tokio::fs::File::open(path).await? },
128 Some(key) => File::EncryptedReader { cryptor_async_reader: CryptorAsyncReader::new(tokio::fs::File::open(path).await?, key) }
129 }
130 )
131 }
132
133 pub async fn create(path: impl AsRef<Path>) -> io::Result<File> {
135 Ok(
136 match get_content_key() {
137 None => File::Plain { tokio_file: tokio::fs::File::create(path).await? },
138 Some(key) => File::EncryptedWriter { cryptor_async_writer: CryptorAsyncWriter::new(tokio::fs::File::create(path).await?, key) }
139 }
140 )
141 }
142
143 pub async fn metadata(&self) -> std::io::Result<Metadata> {
145 match self {
146 File::Plain { tokio_file } => tokio_file.metadata().await,
147 File::EncryptedReader { cryptor_async_reader } => cryptor_async_reader.inner().metadata().await,
148 File::EncryptedWriter { cryptor_async_writer } => cryptor_async_writer.inner().metadata().await,
149 }
150 }
151
152
153}
154
155impl io::AsyncRead for File {
156 fn poll_read(
157 self: Pin<&mut Self>,
158 cx: &mut Context<'_>,
159 dst: &mut io::ReadBuf<'_>,
160 ) -> Poll<io::Result<()>> {
161 match self.get_mut() {
162 Self::Plain { tokio_file } => std::pin::pin!(tokio_file).poll_read(cx, dst),
163 Self::EncryptedReader { cryptor_async_reader } => std::pin::pin!(cryptor_async_reader).poll_read(cx, dst),
164 File::EncryptedWriter { .. } => Poll::Ready(Err(io::Error::new(ErrorKind::Unsupported, "Attempted to read from a file opened exclusively for writing"))),
165 }
166 }
167}
168
169impl io::AsyncWrite for File {
170 fn poll_write(
171 self: Pin<&mut Self>,
172 cx: &mut Context<'_>,
173 src: &[u8],
174 ) -> Poll<std::io::Result<usize>> {
175 match self.get_mut() {
176 Self::Plain { tokio_file } => std::pin::pin!(tokio_file).poll_write(cx, src),
177 File::EncryptedWriter { cryptor_async_writer } => std::pin::pin!(cryptor_async_writer).poll_write(cx, src),
178 Self::EncryptedReader { .. } => Poll::Ready(Err(io::Error::new(ErrorKind::Unsupported, "Attempted to write on a file opened exclusively for reading"))),
179 }
180 }
181
182 fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
183 match self.get_mut() {
184 Self::Plain { tokio_file } => std::pin::pin!(tokio_file).poll_flush(cx),
185 File::EncryptedWriter { cryptor_async_writer } => std::pin::pin!(cryptor_async_writer).poll_flush(cx),
186 Self::EncryptedReader { .. } => Poll::Ready(Err(io::Error::new(ErrorKind::Unsupported, "Attempted to write (flush) a file opened exclusively for reading"))),
187 }
188 }
189
190 fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
191 match self.get_mut() {
192 Self::Plain { tokio_file } => std::pin::pin!(tokio_file).poll_shutdown(cx),
193 File::EncryptedWriter { cryptor_async_writer } => std::pin::pin!(cryptor_async_writer).poll_shutdown(cx),
194 Self::EncryptedReader { .. } => Poll::Ready(Err(io::Error::new(ErrorKind::Unsupported, "Attempted to write (shutdown) on a file opened exclusively for reading"))),
195 }
196 }
197
198}