onetime_cli/otp.rs
1use crate::fs::extend_extension;
2use crate::fs::Mode;
3use crate::fs::{open_file, read, remove_file, write};
4use crate::Config;
5use crate::Error;
6
7use rand::Rng;
8
9/// Encrypts data using the one-time pad.
10///
11/// ## Error
12/// Will return an [`Error::InvalidBufferSizes`] if either
13/// of `buf_out1` or `buf_out2` is smaller than `buf_in`.
14///
15/// ## Example
16/// ```
17/// use onetime_cli::encrypt;
18///
19/// # fn main() -> Result<(), onetime_cli::Error> {
20/// let data: [u8; 10] = [1,2,3,4,5,6,7,8,9,10];
21/// let mut out1 = [0u8; 10];
22/// let mut out2 = [0u8; 10];
23///
24/// encrypt(&data, &mut out1, &mut out2)?;
25/// // The encrypted parts are stored in `out1` and `out2`.
26///
27/// println!("{:?}", out1);
28/// println!("{:?}", out2);
29/// # Ok(())
30/// # }
31/// ```
32pub fn encrypt(buf_in: &[u8], buf_out1: &mut [u8], buf_out2: &mut [u8]) -> Result<(), Error> {
33 if (buf_out1.len() < buf_in.len()) || (buf_out2.len() < buf_in.len()) {
34 return Err(Error::InvalidBufferSizes);
35 }
36
37 let mut rng = rand::thread_rng();
38
39 for i in 0..buf_in.len() {
40 buf_out1[i] = rng.gen_range(0..=255);
41 buf_out2[i] = buf_in[i] ^ buf_out1[i];
42 }
43
44 Ok(())
45}
46
47/// Decrypts data using the one-time pad.
48///
49/// `buf_in1` and `buf_in2` must have the same size.
50///
51/// ## Error
52/// Will return an [`Error::InvalidBufferSizes`] if:
53/// * `buf_in1` and `buf_in2` don't have the same size.
54/// * `buf_out` is smaller than the `buf_in`s.
55///
56/// ## Example
57/// ```
58/// use onetime_cli::decrypt;
59///
60/// # fn main() -> Result<(), onetime_cli::Error> {
61/// let in1: [u8; 10] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
62/// let in2: [u8; 10] = [26, 89, 3, 93, 78, 12, 60, 23, 4, 71];
63/// let mut data = [0u8; 10];
64///
65/// decrypt(&in1, &in2, &mut data)?;
66/// // The decrypted data is stored in `data`.
67///
68/// assert_eq!(data, [27, 91, 0, 89, 75, 10, 59, 31, 13, 77]);
69/// # Ok(())
70/// # }
71/// ```
72pub fn decrypt(buf_in1: &[u8], buf_in2: &[u8], buf_out: &mut [u8]) -> Result<(), Error> {
73 if (buf_in1.len() != buf_in2.len()) || (buf_out.len() < buf_in1.len()) {
74 return Err(Error::InvalidBufferSizes);
75 }
76
77 for i in 0..buf_in1.len() {
78 buf_out[i] = buf_in1[i] ^ buf_in2[i];
79 }
80
81 Ok(())
82}
83
84/// Encrypts a file using the options wrapped in a [`Config`].
85///
86/// ## Errors
87///
88/// Returns an [`Error::IoError`](Error::IoError) if any of the I/O operations fail.
89///
90/// ## Example
91/// ```no_run
92/// use onetime_cli::Config;
93/// use onetime_cli::encrypt_file;
94/// use onetime_cli::Error;
95///
96/// # fn main() -> Result<(), onetime_cli::Error> {
97/// let c = Config::new("secret.txt");
98///
99/// let res = encrypt_file(&c)?;
100/// # Ok(())
101/// # }
102/// ```
103pub fn encrypt_file(c: &Config) -> Result<(), Error> {
104 let mut f_in = open_file(&c.file, Mode::Open)?;
105
106 let f_out1_name = extend_extension(&c.file, &c.suffix1);
107 let f_out2_name = extend_extension(&c.file, &c.suffix2);
108
109 let mut f_out1 = open_file(&f_out1_name, Mode::Create)?;
110 let mut f_out2 = open_file(&f_out2_name, Mode::Create)?;
111
112 let mut buf_in = vec![0u8; c.buffer as usize];
113 let mut buf_out1 = vec![0u8; c.buffer as usize];
114 let mut buf_out2 = vec![0u8; c.buffer as usize];
115
116 loop {
117 let bytes = read(&mut f_in, &mut buf_in)?;
118
119 if bytes == 0 {
120 break;
121 }
122
123 encrypt(
124 &buf_in[..bytes],
125 &mut buf_out1[..bytes],
126 &mut buf_out2[..bytes],
127 )?;
128
129 write(&mut f_out1, &buf_out1[..bytes])?;
130 write(&mut f_out2, &buf_out2[..bytes])?;
131 }
132
133 if c.rm {
134 remove_file(&c.file)?;
135 }
136
137 Ok(())
138}
139
140/// Decrypts a file using the options wrapped in a [`Config`].
141///
142/// ## Errors
143///
144/// Returns an [`Error`] if:
145/// - any of the I/O operations fail ([`IoError`](Error::IoError))
146/// - the two input files differ in length ([`InvalidInput`](Error::InvalidInput))
147///
148/// ## Example
149/// ```no_run
150/// use onetime_cli::Config;
151/// use onetime_cli::decrypt_file;
152/// use onetime_cli::Error;
153///
154/// # fn main() -> Result<(), onetime_cli::Error> {
155/// let c = Config::new("secret.txt");
156///
157/// let res = decrypt_file(&c)?;
158/// # Ok(())
159/// # }
160/// ```
161pub fn decrypt_file(c: &Config) -> Result<(), Error> {
162 let f_in1_name = extend_extension(&c.file, &c.suffix1);
163 let f_in2_name = extend_extension(&c.file, &c.suffix2);
164
165 let mut f_in1 = open_file(&f_in1_name, Mode::Open)?;
166 let mut f_in2 = open_file(&f_in2_name, Mode::Open)?;
167 let mut f_out = open_file(&c.file, Mode::Create)?;
168
169 let mut buf_in1 = vec![0u8; c.buffer as usize];
170 let mut buf_in2 = vec![0u8; c.buffer as usize];
171 let mut buf_out = vec![0u8; c.buffer as usize];
172
173 loop {
174 let bytes_1 = read(&mut f_in1, &mut buf_in1)?;
175 let bytes_2 = read(&mut f_in2, &mut buf_in2)?;
176
177 if bytes_1 != bytes_2 {
178 return Err(Error::InvalidInput(
179 "The two input files differ in size!".to_string(),
180 ));
181 }
182
183 if bytes_1 == 0 {
184 break;
185 }
186
187 decrypt(
188 &buf_in1[..bytes_1],
189 &buf_in2[..bytes_1],
190 &mut buf_out[..bytes_1],
191 )?;
192
193 write(&mut f_out, &buf_out[..bytes_1])?;
194 }
195
196 if c.rm {
197 remove_file(&f_in1_name)?;
198 remove_file(&f_in2_name)?;
199 }
200
201 Ok(())
202}