staticrypt/lib.rs
1//! # Staticrypt
2//!
3//! The name is an abbreviation of "Static Encryption" - a Rust proc macro library to encrypt text
4//! literals or binary data using [`Aes256Gcm`].
5//!
6//! The crate is intended to be a successor to [`litcrypt`](https://docs.rs/litcrypt/latest/litcrypt/),
7//! and expand on the overall idea of the library.
8//!
9//! Like litcrypt, staticrypt works by encrypting the given data at compile time. In its place, it
10//! leaves the encrypted contents and a 96 bit nonce (unique for every encrypted item), protecting
11//! your data from static analysis tools.
12//!
13//! In contrast to to litcrypt's `lc`, staticrypt's [`sc`] supports all valid Rust string literals,
14//! including those with escape sequences, unicode characters, etc.
15//!
16//! To initialize staticrypt in a crate, the [`use_staticrypt`] macro needs to be called first. See
17//! its doc page for more info on initial setup.
18//!
19//! ## Example
20//!
21//! ```rust
22//! use staticrypt::*;
23//!
24//! // Needs to be present at the root of the crate.
25//! use_staticrypt!();
26//!
27//! fn main() {
28//! // Protect sensitive information from static analysis / tampering
29//! println!("The meaning of life is {}", sc!("42"));
30//! }
31//! ```
32//!
33//! Everything inside the [`sc`] macro will be encrypted at compile time. You can verify that none
34//! of the strings are present in cleartext using something like `strings`:
35//!
36//! ```shell
37//! strings target/debug/my_app | grep 42
38//! ```
39//!
40//! If the output is blank / does not contain the string you are looking for, then your app is safe
41//! from static analysis tools.
42//!
43//! ## DISCLAIMER
44//!
45//! Although using tools like staticrypt makes it very difficult for attackers to view or alter
46//! your data, it does _not_ make it impossible. You should develop your programs with the
47//! assumption that a sufficiently determined attacker will be able to reverse engineer your
48//! encryption and gain access to any data present in your binary, so it is **highly discouraged** to
49//! use this crate to embed sensitive information like API keys, passwords, private keys etc. in your
50//! application. You have been warned!
51#![allow(clippy::needless_doctest_main)]
52#![warn(missing_docs)]
53
54use aes_gcm::{Aes256Gcm, Key, KeyInit, aead::Aead};
55
56/// Initializes the use of staticrypt. Should be used at the top level of a crate, i.e. in your
57/// `main.rs` or `lib.rs` (wherever `crate` is pointing to).
58///
59/// This macro will declare a global const named `STATICRYPT_ENCRYPT_KEY` which contains the key
60/// used to decrypt contents encrypted with staticrypt. The key itself is not encrypted.
61///
62/// The key is derived from the seed stored in the environment variable `STATICRYPT_SEED`. If
63/// `STATICRYPT_SEED` is missing, or fewer than 32 characters long, it will be padded with randomly
64/// generated bytes until it is of length 32.
65///
66/// If you desire your builds to be reproducible, set `STATICRYPT_SEED` to contain 32 characters.
67/// This way, both the generated key and all nonces will be predictable.
68pub use staticrypt_macros::use_staticrypt;
69
70/// Encrypts the contained literal string using [`Aes256Gcm`] with the key embedded using
71/// [`use_staticrypt`] and a randomly generated nonce (derived from the `STATICRYPT_SEED` env
72/// variable at compile time).
73///
74/// Example:
75///
76/// ```rust
77/// use staticrypt::*;
78///
79/// use_staticrypt!();
80///
81/// fn main() {
82/// // "My secret text" will be encrypted in the resulting binary
83/// let encrypted = sc!("My secret text");
84///
85/// assert_eq!(encrypted, "My secret text");
86///
87/// // Also works with unicode / non-standard characters, and escape sequences:
88/// let encrypted = sc!("My\0 nonstandard \u{0256} Text \"with escapes\"");
89///
90/// assert_eq!(encrypted, "My\0 nonstandard \u{0256} Text \"with escapes\"");
91/// }
92///
93/// ```
94pub use staticrypt_macros::sc;
95
96/// Reads and encrypts the specified file contents using [`Aes256Gcm`] with the key embedded using
97/// [`use_staticrypt`] and a randomly generated nonce (derived from the `STATICRYPT_SEED` env
98/// variable at compile time).
99///
100/// Note that `sc_bytes` does not do any parsing by default, so it always outputs a
101/// [`Vec<u8>`](std::vec::Vec).
102///
103/// Example:
104///
105/// ```rust
106/// use staticrypt::*;
107///
108/// use_staticrypt!();
109///
110/// fn main() {
111/// let encrypted = String::from_utf8(sc_bytes!("./testfile.txt"))
112/// .expect("Should be valid UTF-8");
113///
114/// assert_eq!(encrypted, "Hello, staticrypt!\n");
115/// }
116/// ```
117pub use staticrypt_macros::sc_bytes;
118
119/// Reads and encrypts the contents of the specified environment variable using [`Aes256Gcm`] with
120/// the key embedded using [`use_staticrypt`] and a randomly generated nonce (derived from the
121/// `STATICRYPT_SEED` variable at compile time).
122///
123/// The contents of the environment variable are read at compile time.
124///
125/// Example:
126///
127/// ```rust
128/// use staticrypt::*;
129///
130/// use_staticrypt!();
131///
132/// fn main() {
133/// let encrypted = sc_env!("MY_SECRET_VAR");
134///
135/// assert_eq!(encrypted, "super secret env");
136/// }
137/// ```
138pub use staticrypt_macros::sc_env;
139
140use_staticrypt!();
141
142/// Decrypt an input with a given nonce and key using [`Aes256Gcm`].
143///
144/// Note that manually calling this function should not be necessary, as the [`sc`] macro already
145/// does this behind the scenes.
146///
147/// # Panics
148///
149/// This function will panic if the decryption fails. This could happen e.g. due to file corruption
150/// of the resulting binary.
151#[must_use]
152pub fn decrypt(input: &[u8], nonce: &[u8], key: &[u8]) -> Vec<u8> {
153 let key = Key::<Aes256Gcm>::from_slice(key);
154 let cipher = Aes256Gcm::new(key);
155
156 cipher.decrypt(nonce.into(), input).unwrap()
157}
158
159#[cfg(test)]
160mod tests {
161 use staticrypt_macros::sc;
162
163 #[test]
164 fn encrypt_decrypt() {
165 let my_text = sc!("super secret woah");
166
167 assert_eq!(my_text, "super secret woah");
168 }
169
170 #[test]
171 fn nullbyte() {
172 let my_text = sc!("sometest\0 withnull");
173
174 assert_eq!(my_text, "sometest\0 withnull");
175 }
176
177 #[test]
178 fn unicode() {
179 let my_text = sc!("I have Unicode \u{0256}");
180
181 assert_eq!(my_text, "I have Unicode \u{0256}");
182 }
183}