diff --git a/.drone.yml b/.drone.yml index b17b35c..6051184 100644 --- a/.drone.yml +++ b/.drone.yml @@ -9,6 +9,6 @@ steps: commands: - rustup component add clippy - cargo clippy -- -D warnings - - cargo test + - cargo test --all-features diff --git a/Cargo.lock b/Cargo.lock index 7c0b6cf..032dbb5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,41 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "433cfd6710c9986c576a25ca913c39d66a6474107b406f34f91d4a8923395241" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e1366e0c69c9f927b1fa5ce2c7bf9eafc8f9268c0b9800729e8b267612447c" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -14,6 +49,25 @@ version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +[[package]] +name = "bincode" +version = "2.0.0-rc.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f11ea1a0346b94ef188834a65c068a03aec181c94896d481d7a0a40d85b0ce95" +dependencies = [ + "bincode_derive", + "serde", +] + +[[package]] +name = "bincode_derive" +version = "2.0.0-rc.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e30759b3b99a1b802a7a3aa21c85c3ded5c28e1c83170d82d70f08bbf7f3e4c" +dependencies = [ + "virtue", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -44,6 +98,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + [[package]] name = "core-foundation" version = "0.9.3" @@ -60,6 +124,35 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +[[package]] +name = "cpufeatures" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "rand_core", + "typenum", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + [[package]] name = "encoding_rs" version = "0.8.32" @@ -168,6 +261,37 @@ dependencies = [ "pin-utils", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "ghash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" +dependencies = [ + "opaque-debug", + "polyval", +] + [[package]] name = "h2" version = "0.3.18" @@ -290,6 +414,15 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + [[package]] name = "instant" version = "0.1.12" @@ -345,10 +478,13 @@ checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" [[package]] name = "light-openid" -version = "0.1.0" +version = "0.1.0-alpha" dependencies = [ + "aes-gcm", "base64", + "bincode", "log", + "rand", "reqwest", "serde", "serde_json", @@ -412,6 +548,12 @@ version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + [[package]] name = "openssl" version = "0.10.52" @@ -480,6 +622,24 @@ version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +[[package]] +name = "polyval" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef234e08c11dfcb2e56f79fd70f6f2eb7f025c0ce2333e82f4f0518ecad30c6" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + [[package]] name = "proc-macro2" version = "1.0.56" @@ -498,6 +658,36 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "redox_syscall" version = "0.3.5" @@ -658,6 +848,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + [[package]] name = "syn" version = "1.0.109" @@ -778,6 +974,12 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + [[package]] name = "unicode-bidi" version = "0.3.13" @@ -799,6 +1001,16 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "universal-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d3160b73c9a19f7e2939a2fdad446c57c1bbbbf4d919d3213ff1267a580d8b5" +dependencies = [ + "crypto-common", + "subtle", +] + [[package]] name = "url" version = "2.3.1" @@ -822,6 +1034,18 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "virtue" +version = "0.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dcc60c0624df774c82a0ef104151231d37da4962957d691c011c852b2473314" + [[package]] name = "want" version = "0.3.0" diff --git a/Cargo.toml b/Cargo.toml index 4ac2fa0..692e97d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,3 +17,11 @@ base64 = "0.21.0" serde = { version = "1.0.160", features = ["derive"] } serde_json = "1.0.96" urlencoding = "2.1.2" + +# Dependencies for crypto wrapper +bincode = { version = "2.0.0-rc.3", optional = true } +aes-gcm = { version = "0.10.1", optional = true } +rand = { version = "0.8.5", optional = true } + +[features] +crypto-wrapper = ["bincode", "aes-gcm", "rand"] \ No newline at end of file diff --git a/src/crypto_wrapper.rs b/src/crypto_wrapper.rs new file mode 100644 index 0000000..6263712 --- /dev/null +++ b/src/crypto_wrapper.rs @@ -0,0 +1,97 @@ +use std::error::Error; +use std::io::ErrorKind; + +use aes_gcm::aead::{Aead, OsRng}; +use aes_gcm::{Aes256Gcm, Key, KeyInit, Nonce}; +use base64::engine::general_purpose::STANDARD as BASE64_STANDARD; +use base64::Engine as _; +pub use bincode::{Decode, Encode}; +use rand::Rng; + +const NONCE_LEN: usize = 12; + +pub struct CryptoWrapper { + key: Key, +} + +impl CryptoWrapper { + /// Generate a new memory wrapper + pub fn new_random() -> Self { + Self { + key: Aes256Gcm::generate_key(&mut OsRng), + } + } + + /// Encrypt some data + pub fn encrypt(&self, data: &T) -> Result> { + let aes_key = Aes256Gcm::new(&self.key); + let nonce_bytes = rand::thread_rng().gen::<[u8; NONCE_LEN]>(); + + let serialized_data = bincode::encode_to_vec(data, bincode::config::standard())?; + + let mut enc = aes_key + .encrypt(Nonce::from_slice(&nonce_bytes), serialized_data.as_slice()) + .unwrap(); + enc.extend_from_slice(&nonce_bytes); + + Ok(BASE64_STANDARD.encode(enc)) + } + + /// Decrypt some data previously encrypted using the [`CryptoWrapper::encrypt`] method + pub fn decrypt(&self, input: &str) -> Result> { + let bytes = BASE64_STANDARD.decode(input)?; + + if bytes.len() < NONCE_LEN { + return Err(Box::new(std::io::Error::new( + ErrorKind::Other, + "Input string is smaller than nonce!", + ))); + } + + let (enc, nonce) = bytes.split_at(bytes.len() - NONCE_LEN); + assert_eq!(nonce.len(), NONCE_LEN); + + let aes_key = Aes256Gcm::new(&self.key); + + let dec = match aes_key.decrypt(Nonce::from_slice(nonce), enc) { + Ok(d) => d, + Err(e) => { + log::error!("Failed to decrypt wrapped data! {:#?}", e); + return Err(Box::new(std::io::Error::new( + ErrorKind::Other, + "Failed to decrypt wrapped data!", + ))); + } + }; + + Ok(bincode::decode_from_slice(&dec, bincode::config::standard())?.0) + } +} + +#[cfg(test)] +mod test { + use crate::crypto_wrapper::CryptoWrapper; + use bincode::{Decode, Encode}; + + #[derive(Encode, Decode, Eq, PartialEq, Debug)] + struct Message(String); + + #[test] + fn encrypt_and_decrypt() { + let wrapper = CryptoWrapper::new_random(); + let msg = Message("Pierre was here".to_string()); + let enc = wrapper.encrypt(&msg).unwrap(); + let dec: Message = wrapper.decrypt(&enc).unwrap(); + + assert_eq!(dec, msg) + } + + #[test] + fn encrypt_and_decrypt_invalid() { + let wrapper_1 = CryptoWrapper::new_random(); + let wrapper_2 = CryptoWrapper::new_random(); + let msg = Message("Pierre was here".to_string()); + let enc = wrapper_1.encrypt(&msg).unwrap(); + wrapper_2.decrypt::(&enc).unwrap_err(); + } +} diff --git a/src/lib.rs b/src/lib.rs index 8dc8b12..ef3892e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,3 +6,6 @@ pub mod client; pub mod primitives; + +#[cfg(feature = "crypto-wrapper")] +pub mod crypto_wrapper;