rexor/lib.rs
1//! This library provides simple file encryption and decryption using XOR logic.
2//! It allows you to encode or decode any file with a defined password,
3//! producing `.rxor` encrypted files.
4//!
5//! # Example: Encoding a file
6//! ```no_run
7//! use rexor::encode;
8//!
9//! fn main() -> std::io::Result<()> {
10//! let input = "example.txt";
11//! let password = "password123";
12//!
13//! // Encode the file into "example.txt.rxor"
14//! let output = encode(input, password, None)?;
15//! println!("Encoded file saved at: {}", output);
16//!
17//! Ok(())
18//! }
19//! ```
20//!
21//! # Example: Decoding a file
22//! ```no_run
23//! use rexor::decode;
24//!
25//! fn main() -> std::io::Result<()> {
26//! let input = "example.txt.rxor";
27//! let password = "password123";
28//!
29//! // Decode the file back to its original form
30//! let output = decode(input, password, None)?;
31//! println!("Decoded file saved at: {}", output);
32//!
33//! Ok(())
34//! }
35//! ```
36//!
37//! # Example: Custom output path
38//! ```no_run
39//! use rexor::{encode, decode};
40//!
41//! fn main() -> std::io::Result<()> {
42//! let input = "example.txt";
43//! let password = "password123";
44//!
45//! // Encode to a custom location
46//! let encoded = encode(input, password, Some("encrypted/output.rxor"))?;
47//!
48//! // Decode back into another file
49//! let decoded = decode(&encoded, password, Some("decrypted/example.txt"))?;
50//!
51//! println!("Encoded: {}", encoded);
52//! println!("Decoded: {}", decoded);
53//!
54//! Ok(())
55//! }
56//! ```
57//!
58//! # Errors
59//! The functions return [`std::io::Result<String>`].
60//! Typical errors include:
61//! - Invalid input paths or missing files
62//! - Empty password ([`std::io::ErrorKind::InvalidInput`])
63//! - I/O issues while reading or writing files
64
65use std::fs::File;
66use std::io::{BufReader, BufWriter, Error, ErrorKind, Read, Result, Write};
67
68/// XOR's all bytes from the input path against the provided password then writes
69/// the result to the output path by proceeding in chunks of 8K.
70fn xor_process(input_path: &str, output_path: &str, password: &str) -> Result<()> {
71 // Opens input + output
72 let mut input_file = BufReader::new(File::open(input_path)?);
73 let mut output_file = BufWriter::new(File::create(output_path)?);
74
75 // Converts the password into bytes
76 let password_bytes = password.as_bytes();
77 if password_bytes.is_empty() {
78 return Err(Error::new(
79 ErrorKind::InvalidInput,
80 "The password cannot be empty",
81 ));
82 }
83
84 // Chunk buffer
85 let mut buffer = [0u8; 8192]; // 8K chunks
86 let mut offset = 0;
87
88 loop {
89 // Reads bytes up to 8K from the input file into the buffer, returns the number of bytes read
90 let bytes_read = input_file.read(&mut buffer)?;
91
92 // Stops the loop when no more bytes can be read from the file
93 if bytes_read == 0 {
94 break;
95 }
96
97 // XOR's each byte with the password
98 for i in 0..bytes_read {
99 buffer[i] ^= password_bytes[(offset + i) % password_bytes.len()];
100 }
101
102 // Writes chunk to the output file
103 output_file.write_all(&buffer[..bytes_read])?;
104 offset += bytes_read;
105 }
106
107 Ok(())
108}
109
110/// Encodes a file using XOR with the provided password and input path.
111/// Returns the output path of the encoded file.
112pub fn encode(input_path: &str, password: &str, output_path: Option<&str>) -> Result<String> {
113 let output = match output_path {
114 Some(path) => path.to_string(),
115 None => format!("{}.rxor", input_path),
116 };
117 xor_process(input_path, &output, password)?;
118 Ok(output)
119}
120
121/// Decodes a previously XOR-encoded file using the provided password.
122/// Returns the output path of the decoded file.
123pub fn decode(input_path: &str, password: &str, output_path: Option<&str>) -> Result<String> {
124 let output = match output_path {
125 Some(path) => path.to_string(),
126 None => {
127 if input_path.ends_with(".rxor") {
128 input_path.strip_suffix(".rxor").unwrap().to_string()
129 } else {
130 input_path.to_string()
131 }
132 }
133 };
134 xor_process(input_path, &output, password)?;
135 Ok(output)
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141 use std::fs::{read, remove_file, write};
142
143 #[test]
144 fn test_encode_decode() {
145 let input = "test.txt";
146 let password = "password123";
147 let original_content = b"Test ReXOR";
148
149 write(input, original_content).unwrap();
150
151 let encoded = encode(input, password, None).unwrap();
152 let decoded = decode(&encoded, password, None).unwrap();
153
154 let result = read(decoded).unwrap();
155 assert_eq!(result, original_content);
156
157 // Cleanup
158 remove_file(input).unwrap();
159 remove_file(encoded).unwrap();
160 }
161}