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}