xorio/
lib.rs

1//! [`Read`]/[`Write`] implementation that Xor's the bytes that come through it and wraps around
2//! another [`Read`] or [`Write`].
3//!
4//! # Examples
5//!
6//! ## Writing
7//!
8//! ```no_run
9//! # use xorio::Xor;
10//! # use std::fs::File;
11//! # use std::io::Write;
12//! let mut file = File::create("my_xored_file.bin").unwrap();
13//! let mut writer = Xor::new(file);
14//! writer.write_all("Hello World".as_bytes());
15//! ```
16//!
17//! ## Reading
18//!
19//! ```no_run
20//! # use xorio::Xor;
21//! # use std::fs::File;
22//! # use std::io::Read;
23//! let mut file = File::open("my_xored_file.bin").unwrap();
24//! let mut reader = Xor::new(file);
25//! let mut content = String::new();
26//! reader.read_to_string(&mut content);
27//! ```
28//!
29//! ## Custom Xor Bytes
30//!
31//! You can also customize the bytes that it will XOR the stream with. By default it uses a single
32//! byte `0b01010101` to calculate the XOR.
33//!
34//! ```no_run
35//! # use xorio::Xor;
36//! # use std::fs::File;
37//! # use std::io::Write;
38//! let mut file = File::create("my_xored_file.bin").unwrap();
39//! let mut writer = Xor::new_with_xor_bytes(file, vec![1, 2, 3]);
40//! writer.write_all("Hello World".as_bytes());
41//! ```
42//!
43//! # License
44//!
45//! This crate is licensed under the [Katharos License][k_license] which places certain
46//! restrictions on what you are allowed to use it for. Please read and understand the terms before
47//! using this crate for your project.
48//!
49//! [k_license]: https://github.com/katharostech/katharos-license
50
51use std::io::{Read, Seek, Write};
52
53/// [`Read`]/[`Write`] implementation that Xor's the bytes that come through it and wraps around
54/// another [`Read`] or [`Write`].
55pub struct Xor<T> {
56    inner: T,
57    xor_bytes: Vec<u8>,
58}
59
60impl<T> Xor<T> {
61    /// Create a new [`Xor`] wrapped around the given IO type.
62    pub fn new(inner: T) -> Self {
63        Xor {
64            inner,
65            xor_bytes: vec![0b01010101],
66        }
67    }
68
69    /// Create a new [`Xor`] wrapped around the given IO type, with custom `xor_bytes` that will be
70    /// used when XOR-ing the stream. The default `xor_bytes` is one `0b01010101` byte.
71    pub fn new_with_xor_bytes(inner: T, xor_bytes: Vec<u8>) -> Self {
72        Xor { inner, xor_bytes }
73    }
74}
75
76impl<R: Read> Read for Xor<R> {
77    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
78        // Read bytes into buf
79        let count = self.inner.read(buf)?;
80
81        // Xor the bytes in the buffer
82        for byte in buf {
83            for xor_byte in &self.xor_bytes {
84                *byte = *byte ^ xor_byte;
85            }
86        }
87
88        Ok(count)
89    }
90}
91
92impl<W: Write> Write for Xor<W> {
93    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
94        self.inner.write(
95            // Write the buffer with each byte XOR-ed
96            buf.iter()
97                .map(|x| {
98                    let mut byte = *x;
99                    for xor_byte in &self.xor_bytes {
100                        byte = byte ^ xor_byte
101                    }
102                    byte
103                })
104                .collect::<Vec<_>>()
105                .as_slice(),
106        )
107    }
108
109    fn flush(&mut self) -> std::io::Result<()> {
110        self.inner.flush()
111    }
112}
113
114impl<S: Seek> Seek for Xor<S> {
115    fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
116        self.inner.seek(pos)
117    }
118}