xorstring/
lib.rs

1//! XOR encrypted strings for Rust.
2//!
3//! These are simply a port of LeFF's C++11 compile-time hacky XOR strings.
4
5#![feature(proc_macro_hygiene)]
6
7/// The XOR string proc-macro used under the hood.
8///
9/// This is available in the case that a raw byte array of the encrypted string
10/// is preferred over an auto-decrypted one.
11pub use xorstring_procmacro;
12
13/// A string which has been encrypted on compile-time and is now to be decrypted
14/// on runtime.
15#[derive(PartialEq, Eq, Debug, Hash, Clone)]
16pub struct XorString<'a> {
17    /// The encrypted string as raw bytes.
18    encrypted: &'a [u8],
19}
20
21impl<'a> XorString<'a> {
22    /// Creates a new instance of a XOR string.
23    ///
24    /// This must not have a runtime calculated value, because that would defeat
25    /// the entire point of this.
26    pub const fn new(encrypted: &'a [u8]) -> Self {
27        XorString { encrypted }
28    }
29
30    /// Decrypt the XorString into a proper string.
31    ///
32    /// This is done at runtime such that this crate has any use.
33    pub fn decrypt(&self) -> String {
34        let mut string = String::with_capacity(self.encrypted.len());
35        let key = xorstring_procmacro::xorstring!();
36
37        for (i, c) in self.encrypted.iter().enumerate() {
38            string.push((c ^ (key as usize + i) as u8) as char);
39        }
40
41        string
42    }
43}
44
45/// Create and encrypt a string at compile-time and prepare it for decryption
46/// on runtime.
47///
48/// This must be called with a byte-string, such as `b"Hello, World!"`.
49#[macro_export]
50macro_rules! xorstring {
51    ($str:literal) => {
52        $crate::XorString::new($crate::xorstring_procmacro::xorstring!($str)).decrypt()
53    };
54}
55
56#[cfg(test)]
57mod tests {
58    #[test]
59    fn test_simple() {
60        let xorred = super::xorstring_procmacro::xorstring!(b"abc");
61        assert_ne!(xorred, b"abc");
62
63        let decrypted = super::XorString::new(xorred);
64        let decrypted: String = decrypted.decrypt();
65        let decrypted = decrypted.as_bytes();
66
67        assert_eq!(decrypted, b"abc");
68    }
69
70    #[test]
71    fn test_macro() {
72        assert_eq!(
73            super::xorstring!(b"Hello, World!"),
74            String::from("Hello, World!"),
75        );
76        assert_ne!(
77            super::xorstring_procmacro::xorstring!(b"Hello, World!"),
78            super::xorstring!(b"Hello, World!").as_bytes(),
79        );
80    }
81}