From ab6c711374569f544e7a12d37e9f1fa909d1eeff Mon Sep 17 00:00:00 2001 From: Vadim Anufriev Date: Mon, 3 Jun 2024 17:48:33 +0400 Subject: [PATCH 01/12] encrypt/decrypt data with secret key --- core/lib/Cargo.toml | 2 + core/lib/src/config/secret_key.rs | 78 ++++++++++++++++++++++++++++++ examples/Cargo.toml | 2 +- examples/private-data/Cargo.toml | 9 ++++ examples/private-data/Rocket.toml | 2 + examples/private-data/src/main.rs | 36 ++++++++++++++ examples/private-data/src/tests.rs | 12 +++++ 7 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 examples/private-data/Cargo.toml create mode 100644 examples/private-data/Rocket.toml create mode 100644 examples/private-data/src/main.rs create mode 100644 examples/private-data/src/tests.rs diff --git a/core/lib/Cargo.toml b/core/lib/Cargo.toml index 004115a681..9903dc3d63 100644 --- a/core/lib/Cargo.toml +++ b/core/lib/Cargo.toml @@ -74,6 +74,8 @@ tokio-stream = { version = "0.1.6", features = ["signal", "time"] } cookie = { version = "0.18", features = ["percent-encode"] } futures = { version = "0.3.30", default-features = false, features = ["std"] } state = "0.6" +aes-gcm = "0.10.3" +base64 = "0.22.1" # tracing tracing = { version = "0.1.40", default-features = false, features = ["std", "attributes"] } diff --git a/core/lib/src/config/secret_key.rs b/core/lib/src/config/secret_key.rs index 46818c4f51..a8c48c76b7 100644 --- a/core/lib/src/config/secret_key.rs +++ b/core/lib/src/config/secret_key.rs @@ -1,10 +1,19 @@ use std::fmt; +use aes_gcm::{Aes256Gcm, Nonce}; +use aes_gcm::aead::{generic_array::GenericArray, Aead, KeyInit}; +use base64::{engine::general_purpose::URL_SAFE, Engine as _}; +use rand::RngCore; + + use cookie::Key; use serde::{de, ser, Deserialize, Serialize}; use crate::request::{Outcome, Request, FromRequest}; +const NONCE_LEN: usize = 12; +const KEY_LEN: usize = 32; + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] enum Kind { Zero, @@ -178,6 +187,75 @@ impl SecretKey { { ser.serialize_bytes(&[0; 32][..]) } + + /// Encrypts the given plaintext. + /// Generates a random nonce for each encryption to ensure uniqueness. + /// Returns the base64-encoded string of the concatenated nonce and ciphertext. + /// + /// # Example + /// ```rust + /// let plaintext = "I like turtles"; + /// let secret_key = SecretKey::generate().expect("error generate key"); + /// + /// let encrypted = secret_key.encrypt(&plaintext).expect("can't encrypt"); + /// let decrypted = secret_key.decrypt(&encrypted).expect("can't decrypt"); + /// + /// assert_eq!(decrypted, plaintext); + /// ``` + pub fn encrypt(&self, plaintext: &str) -> Result { + // Convert the encryption key to a fixed-length array + let key: [u8; KEY_LEN] = self.key.encryption().try_into().map_err(|_| "enc key len error")?; + + // Create a new AES-256-GCM instance with the provided key + let aead = Aes256Gcm::new(GenericArray::from_slice(&key)); + + // Generate a random nonce + let mut nonce = [0u8; NONCE_LEN]; + let mut rng = rand::thread_rng(); + rng.try_fill_bytes(&mut nonce).map_err(|_| "couldn't random fill nonce")?; + let nonce = Nonce::from_slice(&nonce); + + // Encrypt the plaintext using the nonce + let ciphertext = aead.encrypt(nonce, plaintext.as_ref()).map_err(|_| "encryption error")?; + + // Prepare a vector to hold the nonce and ciphertext + let mut encrypted_data = Vec::with_capacity(NONCE_LEN + ciphertext.len()); + encrypted_data.extend_from_slice(nonce); + encrypted_data.extend_from_slice(&ciphertext); + + // Return the base64-encoded result + Ok(URL_SAFE.encode(encrypted_data)) + } + + /// Decrypts the given base64-encoded encrypted data. + /// Extracts the nonce from the data and uses it for decryption. + /// Returns the decrypted plaintext string. + pub fn decrypt(&self, encrypted: &str) -> Result { + // Decode the base64-encoded encrypted data + let decoded = URL_SAFE.decode(encrypted).map_err(|_| "bad base64 value")?; + + // Check if the length of decoded data is at least the length of the nonce + if decoded.len() < NONCE_LEN { + return Err("length of decoded data is < NONCE_LEN"); + } + + // Split the decoded data into nonce and ciphertext + let (nonce, ciphertext) = decoded.split_at(NONCE_LEN); + let nonce = Nonce::from_slice(nonce); + + // Convert the encryption key to a fixed-length array + let key: [u8; KEY_LEN] = self.key.encryption().try_into().expect("enc key len"); + + // Create a new AES-256-GCM instance with the provided key + let aead = Aes256Gcm::new(GenericArray::from_slice(&key)); + + // Decrypt the ciphertext using the nonce + let decrypted = aead.decrypt(nonce, ciphertext) + .map_err(|_| "invalid key/nonce/value: bad seal")?; + + // Convert the decrypted bytes to a UTF-8 string + String::from_utf8(decrypted).map_err(|_| "bad unsealed utf8") + } } impl PartialEq for SecretKey { diff --git a/examples/Cargo.toml b/examples/Cargo.toml index c2ecd5fc59..a833b95998 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -17,8 +17,8 @@ members = [ "testing", "tls", "upgrade", - "pastebin", "todo", "chat", + "private-data", ] diff --git a/examples/private-data/Cargo.toml b/examples/private-data/Cargo.toml new file mode 100644 index 0000000000..83624ad16d --- /dev/null +++ b/examples/private-data/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "private-data" +version = "0.0.0" +workspace = "../" +edition = "2021" +publish = false + +[dependencies] +rocket = { path = "../../core/lib", features = ["secrets"] } diff --git a/examples/private-data/Rocket.toml b/examples/private-data/Rocket.toml new file mode 100644 index 0000000000..ed142545fc --- /dev/null +++ b/examples/private-data/Rocket.toml @@ -0,0 +1,2 @@ +[default] +secret_key = "itlYmFR2vYKrOmFhupMIn/hyB6lYCCTXz4yaQX89XVg=" diff --git a/examples/private-data/src/main.rs b/examples/private-data/src/main.rs new file mode 100644 index 0000000000..cd77b0c571 --- /dev/null +++ b/examples/private-data/src/main.rs @@ -0,0 +1,36 @@ +#[macro_use] +extern crate rocket; + +use rocket::{Config, State}; +use rocket::fairing::AdHoc; + +#[cfg(test)] mod tests; + +#[get("/encrypt/")] +fn encrypt_endpoint(msg: &str, config: &State) -> String{ + let secret_key = config.secret_key.clone(); + let encrypted = secret_key.encrypt(msg).unwrap(); + + info!("received message for encrypt: '{}'", msg); + info!("encrypted msg: '{}'", encrypted); + + encrypted +} + +#[get("/decrypt/")] + fn decrypt_endpoint(msg: &str, config: &State) -> String { + let secret_key = config.secret_key.clone(); + let decrypted = secret_key.decrypt(msg).unwrap(); + + info!("received message for decrypt: '{}'", msg); + info!("decrypted msg: '{}'", decrypted); + + decrypted +} + +#[launch] +fn rocket() -> _ { + rocket::build() + .mount("/", routes![encrypt_endpoint, decrypt_endpoint]) + .attach(AdHoc::config::()) +} diff --git a/examples/private-data/src/tests.rs b/examples/private-data/src/tests.rs new file mode 100644 index 0000000000..c8f000777e --- /dev/null +++ b/examples/private-data/src/tests.rs @@ -0,0 +1,12 @@ +use rocket::local::blocking::Client; + +#[test] +fn encrypt_decrypt() { + let client = Client::tracked(super::rocket()).unwrap(); + let msg = "some-secret-message"; + + let encrypted = client.get(format!("/encrypt/{}", msg)).dispatch().into_string().unwrap(); + let decrypted = client.get(format!("/decrypt/{}", encrypted)).dispatch().into_string().unwrap(); + + assert_eq!(msg, decrypted); +} From b33f56ae3f6b57a5a6ef0882485cd632e1e16882 Mon Sep 17 00:00:00 2001 From: Vadim Anufriev Date: Tue, 4 Jun 2024 16:32:25 +0400 Subject: [PATCH 02/12] change function signatures --- core/lib/Cargo.toml | 1 - core/lib/src/config/secret_key.rs | 28 +++++++++------------ examples/private-data/Cargo.toml | 1 + examples/private-data/src/main.rs | 40 ++++++++++++++++++++++-------- examples/private-data/src/tests.rs | 13 +++++++++- 5 files changed, 55 insertions(+), 28 deletions(-) diff --git a/core/lib/Cargo.toml b/core/lib/Cargo.toml index 9903dc3d63..80436dff88 100644 --- a/core/lib/Cargo.toml +++ b/core/lib/Cargo.toml @@ -75,7 +75,6 @@ cookie = { version = "0.18", features = ["percent-encode"] } futures = { version = "0.3.30", default-features = false, features = ["std"] } state = "0.6" aes-gcm = "0.10.3" -base64 = "0.22.1" # tracing tracing = { version = "0.1.40", default-features = false, features = ["std", "attributes"] } diff --git a/core/lib/src/config/secret_key.rs b/core/lib/src/config/secret_key.rs index a8c48c76b7..4489c93a0f 100644 --- a/core/lib/src/config/secret_key.rs +++ b/core/lib/src/config/secret_key.rs @@ -2,10 +2,7 @@ use std::fmt; use aes_gcm::{Aes256Gcm, Nonce}; use aes_gcm::aead::{generic_array::GenericArray, Aead, KeyInit}; -use base64::{engine::general_purpose::URL_SAFE, Engine as _}; use rand::RngCore; - - use cookie::Key; use serde::{de, ser, Deserialize, Serialize}; @@ -194,7 +191,9 @@ impl SecretKey { /// /// # Example /// ```rust - /// let plaintext = "I like turtles"; + /// use rocket::config::SecretKey; + /// + /// let plaintext = "I like turtles".as_bytes(); /// let secret_key = SecretKey::generate().expect("error generate key"); /// /// let encrypted = secret_key.encrypt(&plaintext).expect("can't encrypt"); @@ -202,7 +201,7 @@ impl SecretKey { /// /// assert_eq!(decrypted, plaintext); /// ``` - pub fn encrypt(&self, plaintext: &str) -> Result { + pub fn encrypt>(&self, value: T) -> Result, &'static str> { // Convert the encryption key to a fixed-length array let key: [u8; KEY_LEN] = self.key.encryption().try_into().map_err(|_| "enc key len error")?; @@ -216,31 +215,29 @@ impl SecretKey { let nonce = Nonce::from_slice(&nonce); // Encrypt the plaintext using the nonce - let ciphertext = aead.encrypt(nonce, plaintext.as_ref()).map_err(|_| "encryption error")?; + let ciphertext = aead.encrypt(nonce, value.as_ref()).map_err(|_| "encryption error")?; // Prepare a vector to hold the nonce and ciphertext let mut encrypted_data = Vec::with_capacity(NONCE_LEN + ciphertext.len()); encrypted_data.extend_from_slice(nonce); encrypted_data.extend_from_slice(&ciphertext); - // Return the base64-encoded result - Ok(URL_SAFE.encode(encrypted_data)) + Ok(encrypted_data) } /// Decrypts the given base64-encoded encrypted data. /// Extracts the nonce from the data and uses it for decryption. /// Returns the decrypted plaintext string. - pub fn decrypt(&self, encrypted: &str) -> Result { - // Decode the base64-encoded encrypted data - let decoded = URL_SAFE.decode(encrypted).map_err(|_| "bad base64 value")?; + pub fn decrypt>(&self, encrypted: T) -> Result, &'static str> { + let encrypted = encrypted.as_ref(); // Check if the length of decoded data is at least the length of the nonce - if decoded.len() < NONCE_LEN { - return Err("length of decoded data is < NONCE_LEN"); + if encrypted.len() <= NONCE_LEN { + return Err("length of encrypted data is <= NONCE_LEN"); } // Split the decoded data into nonce and ciphertext - let (nonce, ciphertext) = decoded.split_at(NONCE_LEN); + let (nonce, ciphertext) = encrypted.split_at(NONCE_LEN); let nonce = Nonce::from_slice(nonce); // Convert the encryption key to a fixed-length array @@ -253,8 +250,7 @@ impl SecretKey { let decrypted = aead.decrypt(nonce, ciphertext) .map_err(|_| "invalid key/nonce/value: bad seal")?; - // Convert the decrypted bytes to a UTF-8 string - String::from_utf8(decrypted).map_err(|_| "bad unsealed utf8") + Ok(decrypted) } } diff --git a/examples/private-data/Cargo.toml b/examples/private-data/Cargo.toml index 83624ad16d..15eea69093 100644 --- a/examples/private-data/Cargo.toml +++ b/examples/private-data/Cargo.toml @@ -7,3 +7,4 @@ publish = false [dependencies] rocket = { path = "../../core/lib", features = ["secrets"] } +base64 = "0.22.1" diff --git a/examples/private-data/src/main.rs b/examples/private-data/src/main.rs index cd77b0c571..e9bb9c7545 100644 --- a/examples/private-data/src/main.rs +++ b/examples/private-data/src/main.rs @@ -3,29 +3,49 @@ extern crate rocket; use rocket::{Config, State}; use rocket::fairing::AdHoc; +use rocket::response::status; +use rocket::http::Status; +use base64::{engine::general_purpose::URL_SAFE, Engine as _}; #[cfg(test)] mod tests; #[get("/encrypt/")] -fn encrypt_endpoint(msg: &str, config: &State) -> String{ +fn encrypt_endpoint(msg: &str, config: &State) -> Result> { let secret_key = config.secret_key.clone(); - let encrypted = secret_key.encrypt(msg).unwrap(); + + let encrypted = secret_key.encrypt(msg).map_err(|_| { + status::Custom(Status::InternalServerError, "Failed to encrypt message".to_string()) + })?; + + let encrypted_msg = URL_SAFE.encode(&encrypted); info!("received message for encrypt: '{}'", msg); - info!("encrypted msg: '{}'", encrypted); + info!("encrypted msg: '{}'", encrypted_msg); - encrypted + Ok(encrypted_msg) } #[get("/decrypt/")] - fn decrypt_endpoint(msg: &str, config: &State) -> String { - let secret_key = config.secret_key.clone(); - let decrypted = secret_key.decrypt(msg).unwrap(); +fn decrypt_endpoint(msg: &str, config: &State) -> Result> { + let secret_key = config.secret_key.clone(); + + let decoded = URL_SAFE.decode(msg).map_err(|_| { + status::Custom(Status::BadRequest, "Failed to decode base64".to_string()) + })?; + + let decrypted = secret_key.decrypt(&decoded).map_err(|_| { + status::Custom(Status::InternalServerError, "Failed to decrypt message".to_string()) + })?; + + let decrypted_msg = String::from_utf8(decrypted).map_err(|_| { + status::Custom(Status::InternalServerError, + "Failed to convert decrypted message to UTF-8".to_string()) + })?; - info!("received message for decrypt: '{}'", msg); - info!("decrypted msg: '{}'", decrypted); + info!("received message for decrypt: '{}'", msg); + info!("decrypted msg: '{}'", decrypted_msg); - decrypted + Ok(decrypted_msg) } #[launch] diff --git a/examples/private-data/src/tests.rs b/examples/private-data/src/tests.rs index c8f000777e..041309022c 100644 --- a/examples/private-data/src/tests.rs +++ b/examples/private-data/src/tests.rs @@ -1,7 +1,18 @@ -use rocket::local::blocking::Client; +use rocket::{config::SecretKey, local::blocking::Client}; #[test] fn encrypt_decrypt() { + let secret_key = SecretKey::generate().unwrap(); + let msg = "very-secret-message".as_bytes(); + + let encrypted = secret_key.encrypt(msg).unwrap(); + let decrypted = secret_key.decrypt(&encrypted).unwrap(); + + assert_eq!(msg, decrypted); +} + +#[test] +fn encrypt_decrypt_api() { let client = Client::tracked(super::rocket()).unwrap(); let msg = "some-secret-message"; From 4bd058b6243678aff1b55b45f01c5eb9f42dc5fd Mon Sep 17 00:00:00 2001 From: Vadim Anufriev Date: Tue, 4 Jun 2024 22:04:34 +0400 Subject: [PATCH 03/12] delete the secret key from the example configs --- examples/cookies/Rocket.toml | 1 - examples/private-data/Rocket.toml | 2 -- 2 files changed, 3 deletions(-) delete mode 100644 examples/private-data/Rocket.toml diff --git a/examples/cookies/Rocket.toml b/examples/cookies/Rocket.toml index 2c5e69c8cd..219cb3734e 100644 --- a/examples/cookies/Rocket.toml +++ b/examples/cookies/Rocket.toml @@ -1,3 +1,2 @@ [default] -secret_key = "itlYmFR2vYKrOmFhupMIn/hyB6lYCCTXz4yaQX89XVg=" template_dir = "templates" diff --git a/examples/private-data/Rocket.toml b/examples/private-data/Rocket.toml deleted file mode 100644 index ed142545fc..0000000000 --- a/examples/private-data/Rocket.toml +++ /dev/null @@ -1,2 +0,0 @@ -[default] -secret_key = "itlYmFR2vYKrOmFhupMIn/hyB6lYCCTXz4yaQX89XVg=" From ffea9d4a45a475a7014f04e1a85e4326af340b2b Mon Sep 17 00:00:00 2001 From: Vadim Anufriev Date: Tue, 4 Jun 2024 22:39:09 +0400 Subject: [PATCH 04/12] change error types --- core/lib/src/config/secret_key.rs | 37 ++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/core/lib/src/config/secret_key.rs b/core/lib/src/config/secret_key.rs index 4489c93a0f..40f9762916 100644 --- a/core/lib/src/config/secret_key.rs +++ b/core/lib/src/config/secret_key.rs @@ -11,6 +11,15 @@ use crate::request::{Outcome, Request, FromRequest}; const NONCE_LEN: usize = 12; const KEY_LEN: usize = 32; +#[derive(Debug)] +pub enum Error { + KeyLengthError, + NonceFillError, + EncryptionError, + DecryptionError, + EncryptedDataLengthError, +} + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] enum Kind { Zero, @@ -194,16 +203,19 @@ impl SecretKey { /// use rocket::config::SecretKey; /// /// let plaintext = "I like turtles".as_bytes(); - /// let secret_key = SecretKey::generate().expect("error generate key"); + /// let secret_key = SecretKey::generate().unwrap(); /// - /// let encrypted = secret_key.encrypt(&plaintext).expect("can't encrypt"); - /// let decrypted = secret_key.decrypt(&encrypted).expect("can't decrypt"); + /// let encrypted = secret_key.encrypt(&plaintext).unwrap(); + /// let decrypted = secret_key.decrypt(&encrypted).unwrap(); /// /// assert_eq!(decrypted, plaintext); /// ``` - pub fn encrypt>(&self, value: T) -> Result, &'static str> { + pub fn encrypt>(&self, value: T) -> Result, Error> { // Convert the encryption key to a fixed-length array - let key: [u8; KEY_LEN] = self.key.encryption().try_into().map_err(|_| "enc key len error")?; + let key: [u8; KEY_LEN] = self.key + .encryption() + .try_into() + .map_err(|_| Error::KeyLengthError)?; // Create a new AES-256-GCM instance with the provided key let aead = Aes256Gcm::new(GenericArray::from_slice(&key)); @@ -211,11 +223,11 @@ impl SecretKey { // Generate a random nonce let mut nonce = [0u8; NONCE_LEN]; let mut rng = rand::thread_rng(); - rng.try_fill_bytes(&mut nonce).map_err(|_| "couldn't random fill nonce")?; + rng.try_fill_bytes(&mut nonce).map_err(|_| Error::NonceFillError)?; let nonce = Nonce::from_slice(&nonce); // Encrypt the plaintext using the nonce - let ciphertext = aead.encrypt(nonce, value.as_ref()).map_err(|_| "encryption error")?; + let ciphertext = aead.encrypt(nonce, value.as_ref()).map_err(|_| Error::EncryptionError)?; // Prepare a vector to hold the nonce and ciphertext let mut encrypted_data = Vec::with_capacity(NONCE_LEN + ciphertext.len()); @@ -228,12 +240,12 @@ impl SecretKey { /// Decrypts the given base64-encoded encrypted data. /// Extracts the nonce from the data and uses it for decryption. /// Returns the decrypted plaintext string. - pub fn decrypt>(&self, encrypted: T) -> Result, &'static str> { + pub fn decrypt>(&self, encrypted: T) -> Result, Error> { let encrypted = encrypted.as_ref(); // Check if the length of decoded data is at least the length of the nonce if encrypted.len() <= NONCE_LEN { - return Err("length of encrypted data is <= NONCE_LEN"); + return Err(Error::EncryptedDataLengthError); } // Split the decoded data into nonce and ciphertext @@ -241,14 +253,17 @@ impl SecretKey { let nonce = Nonce::from_slice(nonce); // Convert the encryption key to a fixed-length array - let key: [u8; KEY_LEN] = self.key.encryption().try_into().expect("enc key len"); + let key: [u8; KEY_LEN] = self.key + .encryption() + .try_into() + .map_err(|_| Error::KeyLengthError)?; // Create a new AES-256-GCM instance with the provided key let aead = Aes256Gcm::new(GenericArray::from_slice(&key)); // Decrypt the ciphertext using the nonce let decrypted = aead.decrypt(nonce, ciphertext) - .map_err(|_| "invalid key/nonce/value: bad seal")?; + .map_err(|_| Error::DecryptionError)?; Ok(decrypted) } From 82786833acf71a2439aea3366f724a82d246c992 Mon Sep 17 00:00:00 2001 From: Vadim Anufriev Date: Wed, 5 Jun 2024 11:57:17 +0400 Subject: [PATCH 05/12] fix doc-comments --- core/lib/src/config/secret_key.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/lib/src/config/secret_key.rs b/core/lib/src/config/secret_key.rs index 40f9762916..7ea2daf77c 100644 --- a/core/lib/src/config/secret_key.rs +++ b/core/lib/src/config/secret_key.rs @@ -194,9 +194,9 @@ impl SecretKey { ser.serialize_bytes(&[0; 32][..]) } - /// Encrypts the given plaintext. + /// Encrypts the given data. /// Generates a random nonce for each encryption to ensure uniqueness. - /// Returns the base64-encoded string of the concatenated nonce and ciphertext. + /// Returns the Vec of the concatenated nonce and ciphertext. /// /// # Example /// ```rust @@ -237,9 +237,9 @@ impl SecretKey { Ok(encrypted_data) } - /// Decrypts the given base64-encoded encrypted data. + /// Decrypts the given encrypted data. /// Extracts the nonce from the data and uses it for decryption. - /// Returns the decrypted plaintext string. + /// Returns the decrypted Vec. pub fn decrypt>(&self, encrypted: T) -> Result, Error> { let encrypted = encrypted.as_ref(); From ac98764b296813691a505decdb86b4aec22694e6 Mon Sep 17 00:00:00 2001 From: Vadim Anufriev Date: Wed, 5 Jun 2024 11:59:08 +0400 Subject: [PATCH 06/12] add tests --- core/lib/tests/private-data.rs | 36 ++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 core/lib/tests/private-data.rs diff --git a/core/lib/tests/private-data.rs b/core/lib/tests/private-data.rs new file mode 100644 index 0000000000..ba7f55a5a9 --- /dev/null +++ b/core/lib/tests/private-data.rs @@ -0,0 +1,36 @@ +#![cfg(feature = "secrets")] +#![deny(warnings)] + +#[cfg(test)] +mod cookies_private_tests { + use rocket::config::SecretKey; + + #[test] + fn encrypt_decrypt() { + let secret_key = SecretKey::generate().unwrap(); + + // encrypt byte array + let msg = "very-secret-message".as_bytes(); + let encrypted = secret_key.encrypt(&msg).unwrap(); + let decrypted = secret_key.decrypt(&encrypted).unwrap(); + assert_eq!(msg, decrypted); + + // encrypt String + let msg = "very-secret-message".to_string(); + let encrypted = secret_key.encrypt(&msg).unwrap(); + let decrypted = secret_key.decrypt(&encrypted).unwrap(); + assert_eq!(msg.as_bytes(), decrypted); + } + + #[test] + fn encrypt_with_wrong_key() { + let msg = "very-secret-message".as_bytes(); + + let secret_key = SecretKey::generate().unwrap(); + let encrypted = secret_key.encrypt(msg).unwrap(); + + let another_secret_key = SecretKey::generate().unwrap(); + let result = another_secret_key.decrypt(&encrypted); + assert!(result.is_err()); + } +} From e08f9f9967d3fd3f30603eafbc6c0011f91e2ddd Mon Sep 17 00:00:00 2001 From: Vadim Anufriev Date: Thu, 6 Jun 2024 17:43:44 +0400 Subject: [PATCH 07/12] use handy method for generate nonce --- core/lib/src/config/secret_key.rs | 16 +++++++--------- core/lib/tests/private-data.rs | 2 +- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/core/lib/src/config/secret_key.rs b/core/lib/src/config/secret_key.rs index 7ea2daf77c..bb7501ea28 100644 --- a/core/lib/src/config/secret_key.rs +++ b/core/lib/src/config/secret_key.rs @@ -1,8 +1,9 @@ use std::fmt; -use aes_gcm::{Aes256Gcm, Nonce}; -use aes_gcm::aead::{generic_array::GenericArray, Aead, KeyInit}; -use rand::RngCore; +use aes_gcm::{ + AeadCore, Aes256Gcm, Nonce, + aead::{generic_array::GenericArray, Aead, KeyInit, OsRng}, +}; use cookie::Key; use serde::{de, ser, Deserialize, Serialize}; @@ -221,17 +222,14 @@ impl SecretKey { let aead = Aes256Gcm::new(GenericArray::from_slice(&key)); // Generate a random nonce - let mut nonce = [0u8; NONCE_LEN]; - let mut rng = rand::thread_rng(); - rng.try_fill_bytes(&mut nonce).map_err(|_| Error::NonceFillError)?; - let nonce = Nonce::from_slice(&nonce); + let nonce = Aes256Gcm::generate_nonce(&mut OsRng); // Encrypt the plaintext using the nonce - let ciphertext = aead.encrypt(nonce, value.as_ref()).map_err(|_| Error::EncryptionError)?; + let ciphertext = aead.encrypt(&nonce, value.as_ref()).map_err(|_| Error::EncryptionError)?; // Prepare a vector to hold the nonce and ciphertext let mut encrypted_data = Vec::with_capacity(NONCE_LEN + ciphertext.len()); - encrypted_data.extend_from_slice(nonce); + encrypted_data.extend_from_slice(nonce.as_slice()); encrypted_data.extend_from_slice(&ciphertext); Ok(encrypted_data) diff --git a/core/lib/tests/private-data.rs b/core/lib/tests/private-data.rs index ba7f55a5a9..52482c4764 100644 --- a/core/lib/tests/private-data.rs +++ b/core/lib/tests/private-data.rs @@ -8,7 +8,7 @@ mod cookies_private_tests { #[test] fn encrypt_decrypt() { let secret_key = SecretKey::generate().unwrap(); - + // encrypt byte array let msg = "very-secret-message".as_bytes(); let encrypted = secret_key.encrypt(&msg).unwrap(); From 4163474e8210fdd20ca659c71a1bc93d04f512f7 Mon Sep 17 00:00:00 2001 From: Vadim Anufriev Date: Fri, 7 Jun 2024 16:47:30 +0400 Subject: [PATCH 08/12] use XChaCha20Poly1305 instead of Aes256Gcm --- core/lib/Cargo.toml | 2 +- core/lib/src/config/secret_key.rs | 27 ++++++++++++--------------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/core/lib/Cargo.toml b/core/lib/Cargo.toml index 80436dff88..3c14866bcc 100644 --- a/core/lib/Cargo.toml +++ b/core/lib/Cargo.toml @@ -74,7 +74,7 @@ tokio-stream = { version = "0.1.6", features = ["signal", "time"] } cookie = { version = "0.18", features = ["percent-encode"] } futures = { version = "0.3.30", default-features = false, features = ["std"] } state = "0.6" -aes-gcm = "0.10.3" +chacha20poly1305 = "0.10.1" # tracing tracing = { version = "0.1.40", default-features = false, features = ["std", "attributes"] } diff --git a/core/lib/src/config/secret_key.rs b/core/lib/src/config/secret_key.rs index bb7501ea28..baea951412 100644 --- a/core/lib/src/config/secret_key.rs +++ b/core/lib/src/config/secret_key.rs @@ -1,15 +1,15 @@ use std::fmt; -use aes_gcm::{ - AeadCore, Aes256Gcm, Nonce, - aead::{generic_array::GenericArray, Aead, KeyInit, OsRng}, +use chacha20poly1305::{ + aead::{Aead, AeadCore, KeyInit, OsRng, generic_array::GenericArray}, + XChaCha20Poly1305, XNonce, }; use cookie::Key; use serde::{de, ser, Deserialize, Serialize}; use crate::request::{Outcome, Request, FromRequest}; -const NONCE_LEN: usize = 12; +const NONCE_LEN: usize = 24; // 192-bit const KEY_LEN: usize = 32; #[derive(Debug)] @@ -218,14 +218,12 @@ impl SecretKey { .try_into() .map_err(|_| Error::KeyLengthError)?; - // Create a new AES-256-GCM instance with the provided key - let aead = Aes256Gcm::new(GenericArray::from_slice(&key)); + let cipher = XChaCha20Poly1305::new(GenericArray::from_slice(&key)); + let nonce = XChaCha20Poly1305::generate_nonce(&mut OsRng); - // Generate a random nonce - let nonce = Aes256Gcm::generate_nonce(&mut OsRng); - - // Encrypt the plaintext using the nonce - let ciphertext = aead.encrypt(&nonce, value.as_ref()).map_err(|_| Error::EncryptionError)?; + let ciphertext = cipher + .encrypt(&nonce, value.as_ref()) + .map_err(|_| Error::EncryptionError)?; // Prepare a vector to hold the nonce and ciphertext let mut encrypted_data = Vec::with_capacity(NONCE_LEN + ciphertext.len()); @@ -248,7 +246,7 @@ impl SecretKey { // Split the decoded data into nonce and ciphertext let (nonce, ciphertext) = encrypted.split_at(NONCE_LEN); - let nonce = Nonce::from_slice(nonce); + let nonce = XNonce::from_slice(nonce); // Convert the encryption key to a fixed-length array let key: [u8; KEY_LEN] = self.key @@ -256,11 +254,10 @@ impl SecretKey { .try_into() .map_err(|_| Error::KeyLengthError)?; - // Create a new AES-256-GCM instance with the provided key - let aead = Aes256Gcm::new(GenericArray::from_slice(&key)); + let cipher = XChaCha20Poly1305::new(GenericArray::from_slice(&key)); // Decrypt the ciphertext using the nonce - let decrypted = aead.decrypt(nonce, ciphertext) + let decrypted = cipher.decrypt(nonce, ciphertext) .map_err(|_| Error::DecryptionError)?; Ok(decrypted) From 4d81f6203e21c393c56bf56da7e817533ee5f1d9 Mon Sep 17 00:00:00 2001 From: Vadim Anufriev Date: Sat, 8 Jun 2024 22:15:54 +0400 Subject: [PATCH 09/12] use hkdf --- core/lib/Cargo.toml | 2 ++ core/lib/src/config/secret_key.rs | 37 +++++++++++++------------------ 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/core/lib/Cargo.toml b/core/lib/Cargo.toml index 3c14866bcc..e2d27f6d0c 100644 --- a/core/lib/Cargo.toml +++ b/core/lib/Cargo.toml @@ -75,6 +75,8 @@ cookie = { version = "0.18", features = ["percent-encode"] } futures = { version = "0.3.30", default-features = false, features = ["std"] } state = "0.6" chacha20poly1305 = "0.10.1" +hkdf = "0.12.4" +sha2 = "0.10.8" # tracing tracing = { version = "0.1.40", default-features = false, features = ["std", "attributes"] } diff --git a/core/lib/src/config/secret_key.rs b/core/lib/src/config/secret_key.rs index baea951412..7279311fb3 100644 --- a/core/lib/src/config/secret_key.rs +++ b/core/lib/src/config/secret_key.rs @@ -1,16 +1,17 @@ use std::fmt; use chacha20poly1305::{ - aead::{Aead, AeadCore, KeyInit, OsRng, generic_array::GenericArray}, - XChaCha20Poly1305, XNonce, + aead::{generic_array::typenum::Unsigned, Aead, AeadCore, KeyInit, OsRng}, + XChaCha20Poly1305, XNonce }; +use hkdf::Hkdf; +use sha2::Sha256; use cookie::Key; use serde::{de, ser, Deserialize, Serialize}; use crate::request::{Outcome, Request, FromRequest}; -const NONCE_LEN: usize = 24; // 192-bit -const KEY_LEN: usize = 32; +const INFO_STRING: &[u8] = b"secret_key_data_encryption"; #[derive(Debug)] pub enum Error { @@ -212,21 +213,18 @@ impl SecretKey { /// assert_eq!(decrypted, plaintext); /// ``` pub fn encrypt>(&self, value: T) -> Result, Error> { - // Convert the encryption key to a fixed-length array - let key: [u8; KEY_LEN] = self.key - .encryption() - .try_into() - .map_err(|_| Error::KeyLengthError)?; - - let cipher = XChaCha20Poly1305::new(GenericArray::from_slice(&key)); let nonce = XChaCha20Poly1305::generate_nonce(&mut OsRng); + let (mut prk, hk) = Hkdf::::extract(Some(&nonce), self.key.encryption()); + hk.expand(INFO_STRING, &mut prk).map_err(|_| Error::KeyLengthError)?; + let cipher = XChaCha20Poly1305::new(&prk); + let ciphertext = cipher .encrypt(&nonce, value.as_ref()) .map_err(|_| Error::EncryptionError)?; // Prepare a vector to hold the nonce and ciphertext - let mut encrypted_data = Vec::with_capacity(NONCE_LEN + ciphertext.len()); + let mut encrypted_data = Vec::with_capacity(nonce.len() + ciphertext.len()); encrypted_data.extend_from_slice(nonce.as_slice()); encrypted_data.extend_from_slice(&ciphertext); @@ -240,21 +238,18 @@ impl SecretKey { let encrypted = encrypted.as_ref(); // Check if the length of decoded data is at least the length of the nonce - if encrypted.len() <= NONCE_LEN { + let nonce_len = ::NonceSize::USIZE; + if encrypted.len() <= nonce_len { return Err(Error::EncryptedDataLengthError); } // Split the decoded data into nonce and ciphertext - let (nonce, ciphertext) = encrypted.split_at(NONCE_LEN); + let (nonce, ciphertext) = encrypted.split_at(nonce_len); let nonce = XNonce::from_slice(nonce); - // Convert the encryption key to a fixed-length array - let key: [u8; KEY_LEN] = self.key - .encryption() - .try_into() - .map_err(|_| Error::KeyLengthError)?; - - let cipher = XChaCha20Poly1305::new(GenericArray::from_slice(&key)); + let (mut prk, hk) = Hkdf::::extract(Some(&nonce), self.key.encryption()); + hk.expand(INFO_STRING, &mut prk).map_err(|_| Error::KeyLengthError)?; + let cipher = XChaCha20Poly1305::new(&prk); // Decrypt the ciphertext using the nonce let decrypted = cipher.decrypt(nonce, ciphertext) From 3c67b978a2e5a0b1609a9b9baf871c91625dfc00 Mon Sep 17 00:00:00 2001 From: Vadim Anufriev Date: Sun, 9 Jun 2024 14:16:12 +0400 Subject: [PATCH 10/12] return Cipher from encrypt --- core/lib/Cargo.toml | 2 + core/lib/src/config/mod.rs | 2 +- core/lib/src/config/secret_key.rs | 119 ++++++++++++++++++++++++++---- core/lib/tests/private-data.rs | 15 +++- examples/private-data/Cargo.toml | 1 - examples/private-data/src/main.rs | 17 +++-- 6 files changed, 131 insertions(+), 25 deletions(-) diff --git a/core/lib/Cargo.toml b/core/lib/Cargo.toml index e2d27f6d0c..cd7055b99d 100644 --- a/core/lib/Cargo.toml +++ b/core/lib/Cargo.toml @@ -77,6 +77,8 @@ state = "0.6" chacha20poly1305 = "0.10.1" hkdf = "0.12.4" sha2 = "0.10.8" +base64 = "0.22.1" +hex = "0.4.3" # tracing tracing = { version = "0.1.40", default-features = false, features = ["std", "attributes"] } diff --git a/core/lib/src/config/mod.rs b/core/lib/src/config/mod.rs index d6969fcb97..acf18ba713 100644 --- a/core/lib/src/config/mod.rs +++ b/core/lib/src/config/mod.rs @@ -138,4 +138,4 @@ mod secret_key; pub use crate::shutdown::Sig; #[cfg(feature = "secrets")] -pub use secret_key::SecretKey; +pub use secret_key::{SecretKey, Cipher}; diff --git a/core/lib/src/config/secret_key.rs b/core/lib/src/config/secret_key.rs index 7279311fb3..d4a2f03615 100644 --- a/core/lib/src/config/secret_key.rs +++ b/core/lib/src/config/secret_key.rs @@ -7,12 +7,11 @@ use chacha20poly1305::{ use hkdf::Hkdf; use sha2::Sha256; use cookie::Key; +use base64::{engine::general_purpose::URL_SAFE, Engine as _}; use serde::{de, ser, Deserialize, Serialize}; use crate::request::{Outcome, Request, FromRequest}; -const INFO_STRING: &[u8] = b"secret_key_data_encryption"; - #[derive(Debug)] pub enum Error { KeyLengthError, @@ -20,6 +19,8 @@ pub enum Error { EncryptionError, DecryptionError, EncryptedDataLengthError, + Base64DecodeError, + HexDecodeError, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] @@ -97,6 +98,38 @@ pub struct SecretKey { provided: bool, } +/// A struct representing encrypted data. +/// +/// The `Cipher` struct encapsulates encrypted data and provides various +/// utility methods for encoding and decoding this data in different formats +/// such as bytes, hexadecimal, and base64. +/// +/// # Examples +/// +/// Creating a `Cipher` from bytes: +/// ``` +/// let data = b"some encrypted data"; +/// let cipher = Cipher::from_bytes(data); +/// ``` +/// +/// Converting a `Cipher` to a hexadecimal string: +/// ``` +/// let hex = cipher.to_hex(); +/// ``` +/// +/// Creating a `Cipher` from a base64 string: +/// ``` +/// let base64_str = "c29tZSBlbmNyeXB0ZWQgZGF0YQ=="; +/// let cipher = Cipher::from_base64(base64_str).unwrap(); +/// ``` +/// +/// Converting a `Cipher` back to bytes: +/// ``` +/// let bytes = cipher.as_bytes(); +/// ``` +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Cipher(Vec); + impl SecretKey { /// Returns a secret key that is all zeroes. pub(crate) fn zero() -> SecretKey { @@ -196,6 +229,13 @@ impl SecretKey { ser.serialize_bytes(&[0; 32][..]) } + fn cipher(&self, nonce: &[u8]) -> Result { + let (mut prk, hk) = Hkdf::::extract(Some(nonce), self.key.encryption()); + hk.expand(b"secret_key_data_encryption", &mut prk).map_err(|_| Error::KeyLengthError)?; + + Ok(XChaCha20Poly1305::new(&prk)) + } + /// Encrypts the given data. /// Generates a random nonce for each encryption to ensure uniqueness. /// Returns the Vec of the concatenated nonce and ciphertext. @@ -207,17 +247,14 @@ impl SecretKey { /// let plaintext = "I like turtles".as_bytes(); /// let secret_key = SecretKey::generate().unwrap(); /// - /// let encrypted = secret_key.encrypt(&plaintext).unwrap(); - /// let decrypted = secret_key.decrypt(&encrypted).unwrap(); + /// let cipher = secret_key.encrypt(&plaintext).unwrap(); + /// let decrypted = secret_key.decrypt(&cipher).unwrap(); /// - /// assert_eq!(decrypted, plaintext); + /// assert_eq!(plaintext, decrypted); /// ``` - pub fn encrypt>(&self, value: T) -> Result, Error> { + pub fn encrypt>(&self, value: T) -> Result { let nonce = XChaCha20Poly1305::generate_nonce(&mut OsRng); - - let (mut prk, hk) = Hkdf::::extract(Some(&nonce), self.key.encryption()); - hk.expand(INFO_STRING, &mut prk).map_err(|_| Error::KeyLengthError)?; - let cipher = XChaCha20Poly1305::new(&prk); + let cipher = self.cipher(&nonce)?; let ciphertext = cipher .encrypt(&nonce, value.as_ref()) @@ -228,7 +265,7 @@ impl SecretKey { encrypted_data.extend_from_slice(nonce.as_slice()); encrypted_data.extend_from_slice(&ciphertext); - Ok(encrypted_data) + Ok(Cipher(encrypted_data)) } /// Decrypts the given encrypted data. @@ -247,9 +284,7 @@ impl SecretKey { let (nonce, ciphertext) = encrypted.split_at(nonce_len); let nonce = XNonce::from_slice(nonce); - let (mut prk, hk) = Hkdf::::extract(Some(&nonce), self.key.encryption()); - hk.expand(INFO_STRING, &mut prk).map_err(|_| Error::KeyLengthError)?; - let cipher = XChaCha20Poly1305::new(&prk); + let cipher = self.cipher(nonce)?; // Decrypt the ciphertext using the nonce let decrypted = cipher.decrypt(nonce, ciphertext) @@ -348,3 +383,59 @@ impl fmt::Debug for SecretKey { ::fmt(self, f) } } + +impl Cipher { + /// Create a `Cipher` from its raw bytes representation. + pub fn from_bytes(bytes: &[u8]) -> Self { + Cipher(bytes.to_vec()) + } + + /// Create a `Cipher` from a vector of bytes. + pub fn from_vec(vec: Vec) -> Self { + Cipher(vec) + } + + /// Create a `Cipher` from a hex string. + pub fn from_hex(hex: &str) -> Result { + let decoded = hex::decode(hex).map_err(|_| Error::HexDecodeError)?; + Ok(Cipher(decoded)) + } + + /// Create a `Cipher` from a base64 string. + pub fn from_base64(base64: &str) -> Result { + let decoded = URL_SAFE.decode(base64).map_err(|_| Error::Base64DecodeError)?; + Ok(Cipher(decoded)) + } + + /// Returns the bytes contained in the `Cipher`. + pub fn as_bytes(&self) -> &[u8] { + &self.0 + } + + /// Consumes the `Cipher` and returns the contained bytes as a vector. + pub fn into_vec(self) -> Vec { + self.0 + } + + /// Returns the hex representation of the bytes contained in the `Cipher`. + pub fn to_hex(&self) -> String { + hex::encode(&self.0) + } + + /// Returns the base64 representation of the bytes contained in the `Cipher`. + pub fn to_base64(&self) -> String { + URL_SAFE.encode(&self.0) + } +} + +impl fmt::Display for Cipher { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.to_base64()) + } +} + +impl AsRef<[u8]> for Cipher { + fn as_ref(&self) -> &[u8] { + self.as_bytes() + } +} \ No newline at end of file diff --git a/core/lib/tests/private-data.rs b/core/lib/tests/private-data.rs index 52482c4764..06ed4b704b 100644 --- a/core/lib/tests/private-data.rs +++ b/core/lib/tests/private-data.rs @@ -3,7 +3,20 @@ #[cfg(test)] mod cookies_private_tests { - use rocket::config::SecretKey; + use rocket::config::{SecretKey, Cipher}; + + #[test] + fn cipher_conversions() { + let secret_key = SecretKey::generate().unwrap(); + + let plaintext = "I like turtles"; + let cipher = secret_key.encrypt(plaintext).unwrap(); + + assert_eq!(cipher, Cipher::from_bytes(&cipher.as_bytes())); + assert_eq!(cipher, Cipher::from_vec(cipher.clone().into_vec())); + assert_eq!(cipher, Cipher::from_hex(&cipher.to_hex()).unwrap()); + assert_eq!(cipher, Cipher::from_base64(&cipher.to_base64()).unwrap()); + } #[test] fn encrypt_decrypt() { diff --git a/examples/private-data/Cargo.toml b/examples/private-data/Cargo.toml index 15eea69093..83624ad16d 100644 --- a/examples/private-data/Cargo.toml +++ b/examples/private-data/Cargo.toml @@ -7,4 +7,3 @@ publish = false [dependencies] rocket = { path = "../../core/lib", features = ["secrets"] } -base64 = "0.22.1" diff --git a/examples/private-data/src/main.rs b/examples/private-data/src/main.rs index e9bb9c7545..32b684c233 100644 --- a/examples/private-data/src/main.rs +++ b/examples/private-data/src/main.rs @@ -1,11 +1,11 @@ #[macro_use] extern crate rocket; +use rocket::config::Cipher; use rocket::{Config, State}; use rocket::fairing::AdHoc; use rocket::response::status; use rocket::http::Status; -use base64::{engine::general_purpose::URL_SAFE, Engine as _}; #[cfg(test)] mod tests; @@ -13,11 +13,12 @@ use base64::{engine::general_purpose::URL_SAFE, Engine as _}; fn encrypt_endpoint(msg: &str, config: &State) -> Result> { let secret_key = config.secret_key.clone(); - let encrypted = secret_key.encrypt(msg).map_err(|_| { - status::Custom(Status::InternalServerError, "Failed to encrypt message".to_string()) - })?; - - let encrypted_msg = URL_SAFE.encode(&encrypted); + let encrypted_msg = secret_key + .encrypt(msg) + .map(|cipher| cipher.to_base64()) + .map_err(|_| { + status::Custom(Status::InternalServerError, "Failed to encrypt message".to_string()) + })?; info!("received message for encrypt: '{}'", msg); info!("encrypted msg: '{}'", encrypted_msg); @@ -29,11 +30,11 @@ fn encrypt_endpoint(msg: &str, config: &State) -> Result) -> Result> { let secret_key = config.secret_key.clone(); - let decoded = URL_SAFE.decode(msg).map_err(|_| { + let cipher = Cipher::from_base64(msg).map_err(|_| { status::Custom(Status::BadRequest, "Failed to decode base64".to_string()) })?; - let decrypted = secret_key.decrypt(&decoded).map_err(|_| { + let decrypted = secret_key.decrypt(&cipher).map_err(|_| { status::Custom(Status::InternalServerError, "Failed to decrypt message".to_string()) })?; From bf5d1cdb48df127ed39c08f04f2e2b50478e4ec0 Mon Sep 17 00:00:00 2001 From: Vadim Anufriev Date: Thu, 13 Jun 2024 16:17:08 +0400 Subject: [PATCH 11/12] use Cipher struct in `decrypt` method --- core/lib/src/config/secret_key.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/core/lib/src/config/secret_key.rs b/core/lib/src/config/secret_key.rs index d4a2f03615..d7a9789d11 100644 --- a/core/lib/src/config/secret_key.rs +++ b/core/lib/src/config/secret_key.rs @@ -268,11 +268,11 @@ impl SecretKey { Ok(Cipher(encrypted_data)) } - /// Decrypts the given encrypted data. + /// Decrypts the given encrypted data, encapsulated in a Cipher wrapper. /// Extracts the nonce from the data and uses it for decryption. /// Returns the decrypted Vec. - pub fn decrypt>(&self, encrypted: T) -> Result, Error> { - let encrypted = encrypted.as_ref(); + pub fn decrypt(&self, encrypted: &Cipher) -> Result, Error> { + let encrypted = encrypted.as_bytes(); // Check if the length of decoded data is at least the length of the nonce let nonce_len = ::NonceSize::USIZE; @@ -433,9 +433,3 @@ impl fmt::Display for Cipher { write!(f, "{}", self.to_base64()) } } - -impl AsRef<[u8]> for Cipher { - fn as_ref(&self) -> &[u8] { - self.as_bytes() - } -} \ No newline at end of file From bba0d6c219dbc9086c4c98019aad8e410ab564ec Mon Sep 17 00:00:00 2001 From: Vadim Anufriev Date: Sun, 7 Jul 2024 13:13:25 +0400 Subject: [PATCH 12/12] move dependencies to `secrets` feature --- core/lib/Cargo.toml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/core/lib/Cargo.toml b/core/lib/Cargo.toml index cd7055b99d..fc72d595d8 100644 --- a/core/lib/Cargo.toml +++ b/core/lib/Cargo.toml @@ -26,7 +26,7 @@ workspace = true default = ["http2", "tokio-macros", "trace"] http2 = ["hyper/http2", "hyper-util/http2"] http3-preview = ["s2n-quic", "s2n-quic-h3", "tls"] -secrets = ["cookie/private", "cookie/key-expansion"] +secrets = ["cookie/private", "cookie/key-expansion", "chacha20poly1305", "hkdf", "sha2", "base64", "hex"] json = ["serde_json"] msgpack = ["rmp-serde"] uuid = ["uuid_", "rocket_http/uuid"] @@ -44,6 +44,13 @@ uuid_ = { package = "uuid", version = "1", optional = true, features = ["serde"] # Optional MTLS dependencies x509-parser = { version = "0.16", optional = true } +# Optional dependencies for "secrets" feature +chacha20poly1305 = { version = "0.10.1", optional = true } +hkdf = { version = "0.12.4", optional = true } +sha2 = { version = "0.10.8", optional = true } +base64 = { version = "0.22.1", optional = true } +hex = { version = "0.4.3", optional = true } + # Hyper dependencies http = "1" bytes = "1.4" @@ -74,11 +81,6 @@ tokio-stream = { version = "0.1.6", features = ["signal", "time"] } cookie = { version = "0.18", features = ["percent-encode"] } futures = { version = "0.3.30", default-features = false, features = ["std"] } state = "0.6" -chacha20poly1305 = "0.10.1" -hkdf = "0.12.4" -sha2 = "0.10.8" -base64 = "0.22.1" -hex = "0.4.3" # tracing tracing = { version = "0.1.40", default-features = false, features = ["std", "attributes"] }